@openlfcp/client 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/identity/context.d.ts +33 -1
- package/dist/identity/context.js +98 -11
- package/dist/identity/devices.d.ts +18 -5
- package/dist/identity/document.d.ts +23 -0
- package/dist/identity/index.d.ts +4 -4
- package/dist/identity/index.js +2 -2
- package/dist/identity/pairing-request.d.ts +12 -0
- package/dist/identity/pairing-request.js +40 -9
- package/dist/identity/pairing.d.ts +11 -0
- package/dist/identity/pairing.js +20 -2
- package/dist/identity/reconcile.d.ts +73 -3
- package/dist/identity/reconcile.js +165 -36
- package/dist/identity/revocation.d.ts +11 -1
- package/dist/identity/revocation.js +69 -13
- package/dist/identity/rotation.d.ts +20 -7
- package/dist/identity/rotation.js +35 -8
- package/package.json +5 -5
|
@@ -3,7 +3,7 @@ import { generateResourceDEK, sha256 } from "@openlfcp/crypto";
|
|
|
3
3
|
import { ABILITY, actsFor, addSequence, checkDataUnit, hasAbility, hasSequence, InMemorySeenUnits, isExposingRevocation, KEY_EPOCH_REASON, parseDataUnit, receiveDataUnit, rotateEpoch, } from "@openlfcp/wire";
|
|
4
4
|
import { snapshotFrontier } from "../outbound.js";
|
|
5
5
|
import { loadControlChain } from "../storage.js";
|
|
6
|
-
import { deliverKeys, fetchDeks, readersAt, runControlPhase, storeDek, withoutEquivocations, } from "./context.js";
|
|
6
|
+
import { deliverKeys, fetchDeks, readersAt, revokedByIdentity, runControlPhase, storeDek, withoutEquivocations, } from "./context.js";
|
|
7
7
|
import { getPendingDek, OperationJournal, PackageLedger, pendingDekRef, phaseOf, putPendingDek, } from "./journal.js";
|
|
8
8
|
import { IdentityUnavailable } from "./network.js";
|
|
9
9
|
import { keyEpochAfter, recordSeq } from "./phases.js";
|
|
@@ -58,6 +58,8 @@ export function exposingRevocationsSince(chain) {
|
|
|
58
58
|
* signature, authorization and the actor chain are checked (0.3.0).
|
|
59
59
|
*/
|
|
60
60
|
export async function verifiedFrontier(channel, chain, local = { units: [], snapshot: [] }, data) {
|
|
61
|
+
if (data?.profile !== undefined)
|
|
62
|
+
checkUnitProfile(data.profile, "FrontierData.profile");
|
|
61
63
|
const R = chain.state.resourceId;
|
|
62
64
|
const opened = await channel.open(R);
|
|
63
65
|
const ranges = opened.kind === "nack"
|
|
@@ -103,7 +105,7 @@ export async function verifiedFrontier(channel, chain, local = { units: [], snap
|
|
|
103
105
|
// covers are attested by it (W §29.3).
|
|
104
106
|
if (!covered &&
|
|
105
107
|
data !== undefined &&
|
|
106
|
-
!(await acceptedAsData(chain, bytes, toHex(check.unitId), local, data)))
|
|
108
|
+
!(await acceptedAsData(chain, bytes, toHex(check.unitId), p.actor, local, data)))
|
|
107
109
|
continue;
|
|
108
110
|
const actor = toHex(p.actor);
|
|
109
111
|
const last = latest.get(actor);
|
|
@@ -164,13 +166,14 @@ const opaque = (dataProfile) => ({
|
|
|
164
166
|
* not decided here means those steps passed). A unit this client cannot
|
|
165
167
|
* decrypt cannot be verified and stays outside the frontier (W §19).
|
|
166
168
|
*/
|
|
167
|
-
async function acceptedAsData(chain, bytes, unitId, local, data) {
|
|
169
|
+
async function acceptedAsData(chain, bytes, unitId, actor, local, data) {
|
|
168
170
|
if (local.accepted?.has(unitId) === true)
|
|
169
171
|
return true;
|
|
170
172
|
if (local.refused?.has(unitId) === true)
|
|
171
173
|
return false;
|
|
174
|
+
// The receiver's own check of this unit: bound to its Resource and signer (W §26.3 rule 7).
|
|
172
175
|
const profile = data.profile !== undefined && data.profile.dataProfile === chain.state.dataProfile
|
|
173
|
-
? data.profile
|
|
176
|
+
? data.profile.codecFor({ resourceId: chain.state.resourceId, actor })
|
|
174
177
|
: opaque(chain.state.dataProfile);
|
|
175
178
|
const r = await receiveDataUnit(chain, bytes, {
|
|
176
179
|
seen: new InMemorySeenUnits(),
|
|
@@ -188,9 +191,14 @@ async function acceptedAsData(chain, bytes, unitId, local, data) {
|
|
|
188
191
|
* reuse it (§9.4). After the ACK the DEK goes to every reader of the new
|
|
189
192
|
* head, each package recorded in the ledger; the revoked device, a
|
|
190
193
|
* consumed or cancelled pairing device and suspended devices are not
|
|
191
|
-
* readers and receive nothing.
|
|
194
|
+
* readers and receive nothing. With `identityChain`, neither does a reader that
|
|
195
|
+
* the rotator's Identity Resource has revoked, or whose chain in the
|
|
196
|
+
* Resource passes through such a device: a pair not yet propagated there
|
|
197
|
+
* (IDENTITY-PROFILE-01 §7.1 step 1) is treated as revoked, and the rotation
|
|
198
|
+
* closes the epoch for it as for the device just revoked.
|
|
192
199
|
*/
|
|
193
200
|
export async function rotateAfter(o) {
|
|
201
|
+
checkFlowProfiles(o);
|
|
194
202
|
const journal = new OperationJournal(o.storage);
|
|
195
203
|
const me = o.installation.signer;
|
|
196
204
|
const R = o.resource;
|
|
@@ -262,7 +270,8 @@ export async function rotateAfter(o) {
|
|
|
262
270
|
ledger: new PackageLedger(o.storage),
|
|
263
271
|
chain,
|
|
264
272
|
sender: me,
|
|
265
|
-
recipients: readersAt(chain.state)
|
|
273
|
+
recipients: readersAt(chain.state).filter((r) => o.identityChain === undefined ||
|
|
274
|
+
!revokedByIdentity(o.identityChain, chain.state, r.principalId)),
|
|
266
275
|
deks: send,
|
|
267
276
|
});
|
|
268
277
|
const phase = phaseOf(await journal.load(o.operation), id);
|
|
@@ -278,11 +287,28 @@ export async function rotateAfter(o) {
|
|
|
278
287
|
* stored accepted units and verified Snapshots, when the storage keeps them),
|
|
279
288
|
* merged into the verified frontier (§8.3).
|
|
280
289
|
*/
|
|
281
|
-
/** The
|
|
290
|
+
/** The profile the application gave for the Resource's Data Profile, if any. */
|
|
282
291
|
const profileFor = (o, chain) => {
|
|
283
|
-
const profile = o.
|
|
292
|
+
const profile = o.profiles?.find((p) => p.dataProfile === chain.state.dataProfile);
|
|
284
293
|
return profile === undefined ? {} : { profile };
|
|
285
294
|
};
|
|
295
|
+
/**
|
|
296
|
+
* A profile given where a per-unit check is needed must be one: a bare codec
|
|
297
|
+
* (0.3.1's FlowOptions.dataProfiles, FrontierData.profile) cannot bind the
|
|
298
|
+
* change's actor to the unit's signer, so it is refused, never used as a
|
|
299
|
+
* weaker check (W §26.3 rule 7).
|
|
300
|
+
*/
|
|
301
|
+
function checkUnitProfile(profile, where) {
|
|
302
|
+
if (typeof profile.codecFor !== "function")
|
|
303
|
+
throw new TypeError(`${where} needs a per-unit profile (dataProfile and codecFor({resourceId, actor})), not a bare codec`);
|
|
304
|
+
}
|
|
305
|
+
/** The flow options of a rotation: 0.3.1's bare `dataProfiles` is refused, never checked less. */
|
|
306
|
+
export function checkFlowProfiles(o) {
|
|
307
|
+
if (o.dataProfiles !== undefined)
|
|
308
|
+
throw new TypeError("FlowOptions.dataProfiles (bare codecs, 0.3.1) is replaced by FlowOptions.profiles (codecFor per unit): a codec without the unit's signer cannot check the change's actor");
|
|
309
|
+
for (const p of o.profiles ?? [])
|
|
310
|
+
checkUnitProfile(p, "FlowOptions.profiles");
|
|
311
|
+
}
|
|
286
312
|
async function localAccepted(o, chain) {
|
|
287
313
|
const st = o.storage;
|
|
288
314
|
if (st.dataUnits === undefined || st.snapshots === undefined)
|
|
@@ -328,6 +354,7 @@ async function localAccepted(o, chain) {
|
|
|
328
354
|
* it (§9.1, §9.2). Run before planning new work on a Resource.
|
|
329
355
|
*/
|
|
330
356
|
export async function resumeRotations(o) {
|
|
357
|
+
checkFlowProfiles(o);
|
|
331
358
|
const journal = new OperationJournal(o.storage);
|
|
332
359
|
const out = [];
|
|
333
360
|
for (const op of await journal.list())
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openlfcp/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "LFCP client: session, Control Plane and Data Plane synchronization.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openlfcp",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"tag": "latest"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@openlfcp/core": "^0.
|
|
46
|
-
"@openlfcp/crypto": "^0.
|
|
47
|
-
"@openlfcp/storage": "^0.
|
|
48
|
-
"@openlfcp/wire": "^0.
|
|
45
|
+
"@openlfcp/core": "^0.5.0",
|
|
46
|
+
"@openlfcp/crypto": "^0.5.0",
|
|
47
|
+
"@openlfcp/storage": "^0.5.0",
|
|
48
|
+
"@openlfcp/wire": "^0.5.0"
|
|
49
49
|
}
|
|
50
50
|
}
|