@serfab/cadre-core 1.13.0 → 1.15.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/README.md +293 -274
- package/dist/cadre-invite-protocol.d.ts +383 -0
- package/dist/cadre-invite-protocol.js +774 -0
- package/dist/cadre-invite-protocol.js.map +1 -0
- package/dist/cadre-join-request.d.ts +42 -0
- package/dist/cadre-join-request.js +144 -0
- package/dist/cadre-join-request.js.map +1 -0
- package/dist/cadre-node.d.ts +849 -210
- package/dist/cadre-node.js +1574 -374
- package/dist/cadre-node.js.map +1 -1
- package/dist/claim-proof.d.ts +45 -0
- package/dist/claim-proof.js +132 -0
- package/dist/claim-proof.js.map +1 -0
- package/dist/control-authorization.d.ts +80 -19
- package/dist/control-authorization.js +55 -13
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.js +1 -1
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +605 -108
- package/dist/control-database.js +1299 -280
- package/dist/control-database.js.map +1 -1
- package/dist/control-protocol-guard.d.ts +142 -0
- package/dist/control-protocol-guard.js +293 -0
- package/dist/control-protocol-guard.js.map +1 -0
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +817 -1067
- package/dist/control-schema.js.map +1 -1
- package/dist/control-stream.d.ts +4 -0
- package/dist/control-stream.js +22 -7
- package/dist/control-stream.js.map +1 -1
- package/dist/delegate-admission.d.ts +1 -1
- package/dist/delegate-admission.js +1 -1
- package/dist/device-token.d.ts +11 -9
- package/dist/device-token.js +14 -12
- package/dist/device-token.js.map +1 -1
- package/dist/ed25519-key.d.ts +22 -9
- package/dist/ed25519-key.js +47 -10
- package/dist/ed25519-key.js.map +1 -1
- package/dist/fetch-impl.d.ts +12 -0
- package/dist/fetch-impl.js +23 -0
- package/dist/fetch-impl.js.map +1 -0
- package/dist/file-durable-slot.js +2 -1
- package/dist/file-durable-slot.js.map +1 -1
- package/dist/file-safe-name.d.ts +25 -0
- package/dist/file-safe-name.js +29 -0
- package/dist/file-safe-name.js.map +1 -0
- package/dist/formation-approval.js +5 -11
- package/dist/formation-approval.js.map +1 -1
- package/dist/fs-atomic.d.ts +0 -19
- package/dist/fs-atomic.js +3 -24
- package/dist/fs-atomic.js.map +1 -1
- package/dist/index.d.ts +22 -10
- package/dist/index.js +54 -11
- package/dist/index.js.map +1 -1
- package/dist/invitation-bootstrap.d.ts +50 -12
- package/dist/invitation-bootstrap.js +76 -13
- package/dist/invitation-bootstrap.js.map +1 -1
- package/dist/joined-strand-store.d.ts +7 -0
- package/dist/joined-strand-store.js +9 -0
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/key-store-file.js +2 -1
- package/dist/key-store-file.js.map +1 -1
- package/dist/link-budget.d.ts +25 -33
- package/dist/link-budget.js +25 -35
- package/dist/link-budget.js.map +1 -1
- package/dist/machine-removal.d.ts +78 -0
- package/dist/machine-removal.js +166 -0
- package/dist/machine-removal.js.map +1 -0
- package/dist/membership-connection-gater.d.ts +191 -151
- package/dist/membership-connection-gater.js +236 -173
- package/dist/membership-connection-gater.js.map +1 -1
- package/dist/node-claim-payload.d.ts +74 -0
- package/dist/node-claim-payload.js +224 -0
- package/dist/node-claim-payload.js.map +1 -0
- package/dist/node-default-transports.d.ts +27 -0
- package/dist/node-default-transports.js +35 -0
- package/dist/node-default-transports.js.map +1 -0
- package/dist/node-transports.d.ts +30 -0
- package/dist/node-transports.js +40 -0
- package/dist/node-transports.js.map +1 -0
- package/dist/owner-anchor-sync.d.ts +22 -0
- package/dist/owner-anchor-sync.js +143 -0
- package/dist/owner-anchor-sync.js.map +1 -0
- package/dist/peer-authorization.d.ts +113 -18
- package/dist/peer-authorization.js +225 -29
- package/dist/peer-authorization.js.map +1 -1
- package/dist/peer-dial.d.ts +143 -27
- package/dist/peer-dial.js +276 -31
- package/dist/peer-dial.js.map +1 -1
- package/dist/peer-join-backfill.d.ts +26 -2
- package/dist/peer-join-backfill.js +60 -12
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/peer-record.d.ts +17 -52
- package/dist/peer-record.js +20 -58
- package/dist/peer-record.js.map +1 -1
- package/dist/pending-join-runner.d.ts +66 -58
- package/dist/pending-join-runner.js +136 -145
- package/dist/pending-join-runner.js.map +1 -1
- package/dist/primary-lan-address.d.ts +8 -0
- package/dist/primary-lan-address.js +46 -0
- package/dist/primary-lan-address.js.map +1 -0
- package/dist/provider/client.d.ts +131 -0
- package/dist/provider/client.js +226 -0
- package/dist/provider/client.js.map +1 -0
- package/dist/provider/descriptor.d.ts +41 -0
- package/dist/provider/descriptor.js +103 -0
- package/dist/provider/descriptor.js.map +1 -0
- package/dist/provider/index.d.ts +9 -0
- package/dist/provider/index.js +10 -0
- package/dist/provider/index.js.map +1 -0
- package/dist/provider/oauth.d.ts +125 -0
- package/dist/provider/oauth.js +199 -0
- package/dist/provider/oauth.js.map +1 -0
- package/dist/provider/rent-node.d.ts +101 -0
- package/dist/provider/rent-node.js +220 -0
- package/dist/provider/rent-node.js.map +1 -0
- package/dist/push-fanout.d.ts +8 -2
- package/dist/push-fanout.js +8 -2
- package/dist/push-fanout.js.map +1 -1
- package/dist/relay-addrs.d.ts +9 -4
- package/dist/relay-addrs.js +9 -4
- package/dist/relay-addrs.js.map +1 -1
- package/dist/relay-reservation.d.ts +51 -16
- package/dist/relay-reservation.js +107 -24
- package/dist/relay-reservation.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +165 -123
- package/dist/seed-bootstrap.js +313 -275
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/seed-trust-policy.d.ts +98 -37
- package/dist/seed-trust-policy.js +188 -2
- package/dist/seed-trust-policy.js.map +1 -1
- package/dist/strand-addr-protocol.d.ts +24 -26
- package/dist/strand-addr-protocol.js +19 -26
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-commit-durability.d.ts +54 -0
- package/dist/strand-commit-durability.js +74 -0
- package/dist/strand-commit-durability.js.map +1 -0
- package/dist/strand-database.d.ts +23 -8
- package/dist/strand-database.js +41 -13
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +7 -1
- package/dist/strand-first-sync-gate.js +7 -1
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-protocol.js +5 -2
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-fret-addrs.d.ts +19 -1
- package/dist/strand-fret-addrs.js +27 -9
- package/dist/strand-fret-addrs.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +81 -19
- package/dist/strand-instance-manager.js +216 -105
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-move.d.ts +124 -0
- package/dist/strand-move.js +438 -0
- package/dist/strand-move.js.map +1 -0
- package/dist/strand-network-config.js +7 -6
- package/dist/strand-network-config.js.map +1 -1
- package/dist/strand-peer-redial.d.ts +113 -0
- package/dist/strand-peer-redial.js +236 -0
- package/dist/strand-peer-redial.js.map +1 -0
- package/dist/strand-revocation-enforcer.d.ts +11 -11
- package/dist/strand-revocation-enforcer.js +11 -11
- package/dist/strand-wake-protocol.d.ts +13 -16
- package/dist/strand-wake-protocol.js +23 -27
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/trusted-owner-store.d.ts +35 -5
- package/dist/trusted-owner-store.js +50 -8
- package/dist/trusted-owner-store.js.map +1 -1
- package/dist/types.d.ts +512 -89
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -1
- package/package.json +19 -10
- package/src/cadre-invite-protocol.ts +1052 -0
- package/src/cadre-join-request.ts +167 -0
- package/src/cadre-node.ts +1703 -386
- package/src/claim-proof.ts +147 -0
- package/src/control-authorization.ts +203 -117
- package/src/control-cohort.ts +1 -1
- package/src/control-database.ts +4663 -3453
- package/src/control-protocol-guard.ts +348 -0
- package/src/control-schema.ts +828 -1078
- package/src/control-stream.ts +26 -7
- package/src/delegate-admission.ts +1 -1
- package/src/device-token.ts +14 -10
- package/src/ed25519-key.ts +47 -10
- package/src/fetch-impl.ts +23 -0
- package/src/file-durable-slot.ts +64 -63
- package/src/file-safe-name.ts +32 -0
- package/src/formation-approval.ts +604 -610
- package/src/fs-atomic.ts +92 -117
- package/src/index.ts +789 -648
- package/src/invitation-bootstrap.ts +91 -13
- package/src/joined-strand-store.ts +508 -498
- package/src/key-store-file.ts +142 -141
- package/src/link-budget.ts +25 -36
- package/src/machine-removal.ts +216 -0
- package/src/membership-connection-gater.ts +259 -191
- package/src/node-claim-payload.ts +259 -0
- package/src/node-default-transports.ts +38 -0
- package/src/node-transports.ts +49 -0
- package/src/owner-anchor-sync.ts +171 -0
- package/src/peer-authorization.ts +258 -29
- package/src/peer-dial.ts +604 -315
- package/src/peer-join-backfill.ts +62 -11
- package/src/peer-record.ts +159 -197
- package/src/pending-join-runner.ts +729 -735
- package/src/primary-lan-address.ts +47 -0
- package/src/provider/client.ts +326 -0
- package/src/provider/descriptor.ts +129 -0
- package/src/provider/index.ts +59 -0
- package/src/provider/oauth.ts +285 -0
- package/src/provider/rent-node.ts +275 -0
- package/src/push-fanout.ts +8 -2
- package/src/relay-addrs.ts +9 -4
- package/src/relay-reservation.ts +1211 -1121
- package/src/seed-bootstrap.ts +1502 -1448
- package/src/seed-trust-policy.ts +353 -140
- package/src/strand-addr-protocol.ts +26 -35
- package/src/strand-commit-durability.ts +116 -0
- package/src/strand-database.ts +46 -14
- package/src/strand-first-sync-gate.ts +7 -1
- package/src/strand-formation-protocol.ts +1039 -1036
- package/src/strand-fret-addrs.ts +179 -145
- package/src/strand-instance-manager.ts +2026 -1883
- package/src/strand-move.ts +585 -0
- package/src/strand-network-config.ts +7 -6
- package/src/strand-peer-redial.ts +307 -0
- package/src/strand-revocation-enforcer.ts +11 -11
- package/src/strand-wake-protocol.ts +23 -30
- package/src/trusted-owner-store.ts +242 -171
- package/src/types.ts +2687 -2242
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { RevocableTable } from './control-authorization.js';
|
|
1
|
+
import type { CadreInviteSignedFields, RevocableTable } from './control-authorization.js';
|
|
2
|
+
import type { CadreInviteRow, CadreInviteUsageRow, CadrePeerVoucherFields, OwnerKeyRow, RevocationRow } from './types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Canonical digest an owner signs to vouch a peer's ENROLLMENT — the offline
|
|
4
5
|
* credential `cadre enroll register` verifies. No table checks this digest, so it
|
|
@@ -34,7 +35,7 @@ export interface DeviceTokenAuthorizedRow {
|
|
|
34
35
|
/**
|
|
35
36
|
* Canonical digest an owner signs to authorize a `DeviceToken` INSERT — the WHOLE row,
|
|
36
37
|
* ending in its single-use `StampId` nonce. SQL mirror:
|
|
37
|
-
* `digest('CadreControl.DeviceToken', 'add', new.PeerId, new.Platform, new.Token,
|
|
38
|
+
* `digest('CadreControl.DeviceToken', 'add', party_id(), new.PeerId, new.Platform, new.Token,
|
|
38
39
|
* coalesce(cast(new.UpdatedAt as text), ''), coalesce(new.Sig, ''), new.StampId)` in
|
|
39
40
|
* `DeviceToken.AuthorizedInsert`.
|
|
40
41
|
*
|
|
@@ -50,11 +51,11 @@ export interface DeviceTokenAuthorizedRow {
|
|
|
50
51
|
* Distinct from {@link deviceTokenRemoveDigest} so a captured insert approval can never
|
|
51
52
|
* be replayed to delete the token, and vice versa.
|
|
52
53
|
*/
|
|
53
|
-
export declare function deviceTokenAddDigest(row: DeviceTokenAuthorizedRow): string;
|
|
54
|
+
export declare function deviceTokenAddDigest(partyId: string, row: DeviceTokenAuthorizedRow): string;
|
|
54
55
|
/**
|
|
55
56
|
* Canonical digest an owner signs to authorize a `DeviceToken` DELETE, bound to the
|
|
56
57
|
* STORED row's (PeerId, StampId). SQL mirror:
|
|
57
|
-
* `digest('CadreControl.DeviceToken', 'remove', old.PeerId, old.StampId)` in
|
|
58
|
+
* `digest('CadreControl.DeviceToken', 'remove', party_id(), old.PeerId, old.StampId)` in
|
|
58
59
|
* `DeviceToken.AuthorizedDelete`.
|
|
59
60
|
*
|
|
60
61
|
* A narrower vector than {@link deviceTokenAddDigest} on purpose (the same split
|
|
@@ -62,7 +63,7 @@ export declare function deviceTokenAddDigest(row: DeviceTokenAuthorizedRow): str
|
|
|
62
63
|
* cannot be re-cut into an insert approval, and it is dead the moment the stamp it
|
|
63
64
|
* names is tombstoned.
|
|
64
65
|
*/
|
|
65
|
-
export declare function deviceTokenRemoveDigest(peerId: string, stampId: string): string;
|
|
66
|
+
export declare function deviceTokenRemoveDigest(partyId: string, peerId: string, stampId: string): string;
|
|
66
67
|
/**
|
|
67
68
|
* Canonical digest an owner signs to VOUCH a `CadrePeer` membership row (insert
|
|
68
69
|
* and the owner re-touch update — same semantics, deliberately the same digest).
|
|
@@ -74,9 +75,9 @@ export declare function deviceTokenRemoveDigest(peerId: string, stampId: string)
|
|
|
74
75
|
* a delete. The domain tag keeps the stored, replicated `VouchSig` useless against
|
|
75
76
|
* every OTHER table's rules.
|
|
76
77
|
*
|
|
77
|
-
* SQL mirror: `digest('CadreControl.CadrePeer', 'vouch', new.PeerId, new.StampId)`.
|
|
78
|
+
* SQL mirror: `digest('CadreControl.CadrePeer', 'vouch', party_id(), new.PeerId, new.StampId)`.
|
|
78
79
|
*/
|
|
79
|
-
export declare function cadrePeerVoucherDigest(peerId: string, stampId: string): string;
|
|
80
|
+
export declare function cadrePeerVoucherDigest(partyId: string, peerId: string, stampId: string): string;
|
|
80
81
|
/**
|
|
81
82
|
* Canonical digest an owner signs to REMOVE a `CadrePeer` row. Deliberately a
|
|
82
83
|
* distinct payload from {@link cadrePeerVoucherDigest} (the `'remove'` action tag)
|
|
@@ -86,16 +87,16 @@ export declare function cadrePeerVoucherDigest(peerId: string, stampId: string):
|
|
|
86
87
|
* delete lands, because a re-added row carries a FRESH `StampId` (the removed row's
|
|
87
88
|
* stamp is retired into `CadreControl.Revocation` and never reused).
|
|
88
89
|
*
|
|
89
|
-
* SQL mirror: `digest('CadreControl.CadrePeer', 'remove', old.PeerId, old.StampId)`.
|
|
90
|
+
* SQL mirror: `digest('CadreControl.CadrePeer', 'remove', party_id(), old.PeerId, old.StampId)`.
|
|
90
91
|
*/
|
|
91
|
-
export declare function cadrePeerRemoveDigest(peerId: string, stampId: string): string;
|
|
92
|
+
export declare function cadrePeerRemoveDigest(partyId: string, peerId: string, stampId: string): string;
|
|
92
93
|
/**
|
|
93
94
|
* Canonical digest an owner signs to APPEND a `CadreControl.Revocation` tombstone —
|
|
94
95
|
* the row retiring `stampId` for the named guarded table, and recording `rowKey`
|
|
95
96
|
* (the removed row's primary key: OwnerKey.Key / ValidationKey.Key /
|
|
96
97
|
* CadrePeer.PeerId / DeviceToken.PeerId / Strand.Id) as which row was retired.
|
|
97
98
|
* SQL mirror:
|
|
98
|
-
* `digest('CadreControl.Revocation', 'remove', new.TableName, new.RowKey, new.StampId)`
|
|
99
|
+
* `digest('CadreControl.Revocation', 'remove', party_id(), new.TableName, new.RowKey, new.StampId)`
|
|
99
100
|
* in `Revocation.Authorized`.
|
|
100
101
|
*
|
|
101
102
|
* Its own `'CadreControl.Revocation'` domain tag makes it disjoint from the
|
|
@@ -104,7 +105,28 @@ export declare function cadrePeerRemoveDigest(peerId: string, stampId: string):
|
|
|
104
105
|
* this tombstone accompanies — a removal signature is not a retirement
|
|
105
106
|
* signature and cannot be replayed as one.
|
|
106
107
|
*/
|
|
107
|
-
export declare function revocationDigest(tableName: RevocableTable, rowKey: string, stampId: string): string;
|
|
108
|
+
export declare function revocationDigest(partyId: string, tableName: RevocableTable, rowKey: string, stampId: string): string;
|
|
109
|
+
/**
|
|
110
|
+
* Does a `Revocation` row's stored signer pair (`SignerKey` / `SignerSig`) verify over the
|
|
111
|
+
* tombstone's own triple, for `partyId`? The read-side mirror of `Revocation.Authorized`,
|
|
112
|
+
* for a node that received the row by replication and must decide whether to honour it.
|
|
113
|
+
* Whether `row.signerKey` is an owner this node trusts is the caller's question, as for
|
|
114
|
+
* {@link verifyCadrePeerVoucher}. Never throws: a malformed row verifies as `false`.
|
|
115
|
+
*/
|
|
116
|
+
export declare function verifyRevocationSigner(partyId: string, row: RevocationRow): boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Canonical digest an owner signs to seat a further `OwnerKey` row — the signed-add branch
|
|
119
|
+
* of `OwnerKey.Authorized`, which stores the pair as `VouchOwner` / `VouchSig`. SQL mirror:
|
|
120
|
+
* `digest('CadreControl.OwnerKey', 'add', party_id(), new.Key, new.StampId)`. The removal
|
|
121
|
+
* digest needs no builder here: it is the generic guarded `'remove'` digest over (Key, StampId).
|
|
122
|
+
*/
|
|
123
|
+
export declare function ownerKeyAddDigest(partyId: string, key: string, stampId: string): string;
|
|
124
|
+
/**
|
|
125
|
+
* Does an owner-signed `OwnerKey` row's stored voucher verify — `vouchSig` by `vouchOwner`
|
|
126
|
+
* over {@link ownerKeyAddDigest} for (`partyId`, `key`, `stampId`)? Whether `vouchOwner` is
|
|
127
|
+
* anchored is the caller's question, as for {@link verifyCadrePeerVoucher}. Never throws.
|
|
128
|
+
*/
|
|
129
|
+
export declare function verifyOwnerKeyVoucher(partyId: string, key: string, stampId: string, vouchOwner: string, vouchSig: string): boolean;
|
|
108
130
|
/**
|
|
109
131
|
* Verify that `signature` is a valid owner ed25519 signature over `peerId`'s
|
|
110
132
|
* authorization digest, using `ownerPublicKey` (base64url).
|
|
@@ -122,12 +144,13 @@ export declare function revocationDigest(tableName: RevocableTable, rowKey: stri
|
|
|
122
144
|
export declare function verifyPeerAuthorization(peerId: string, ownerPublicKey: string, signature: string): boolean;
|
|
123
145
|
/**
|
|
124
146
|
* Verify that `signature` is a valid owner ed25519 signature over the
|
|
125
|
-
* `CadrePeer` voucher digest for (`peerId`, `stampId`) — the read-side
|
|
126
|
-
* of the voucher {@link ControlDatabase.insertCadrePeer} signs and persists into
|
|
147
|
+
* `CadrePeer` voucher digest for (`partyId`, `peerId`, `stampId`) — the read-side
|
|
148
|
+
* mirror of the voucher {@link ControlDatabase.insertCadrePeer} signs and persists into
|
|
127
149
|
* `VouchOwner`/`VouchSig` (see {@link cadrePeerVoucherDigest}).
|
|
128
150
|
*
|
|
129
151
|
* A `true` result means the holder of `ownerPublicKey` vouched THIS membership
|
|
130
|
-
* row (the peer id bound to the row's single-use `StampId` nonce)
|
|
152
|
+
* row (the peer id bound to the row's single-use `StampId` nonce) in THIS party: a
|
|
153
|
+
* voucher copied from another party's table that shares the owner key fails. It says
|
|
131
154
|
* nothing about whether that owner key is itself trustworthy — the caller must
|
|
132
155
|
* separately check the key against the node-local trusted-owner anchor
|
|
133
156
|
* (`TrustedOwnerStore`), never the replicated `OwnerKey` table.
|
|
@@ -136,14 +159,14 @@ export declare function verifyPeerAuthorization(peerId: string, ownerPublicKey:
|
|
|
136
159
|
* {@link verifyPeerAuthorization}): malformed input or any crypto failure
|
|
137
160
|
* resolves to `false`, logged at debug.
|
|
138
161
|
*/
|
|
139
|
-
export declare function verifyCadrePeerVoucher(peerId: string, stampId: string, ownerPublicKey: string, signature: string): boolean;
|
|
162
|
+
export declare function verifyCadrePeerVoucher(partyId: string, peerId: string, stampId: string, ownerPublicKey: string, signature: string): boolean;
|
|
140
163
|
/**
|
|
141
164
|
* Canonical digest a JOINING peer signs to consent to ONE `FormationUsage`
|
|
142
165
|
* redemption — the read-side mirror of `formationConsentMessage` in
|
|
143
166
|
* control-database.ts, base64url-encoded instead of raw bytes (see
|
|
144
|
-
* {@link
|
|
145
|
-
* (notably why `strandId` is
|
|
146
|
-
* peer-authorization.spec.ts pins by signing one form and verifying the other.
|
|
167
|
+
* {@link unboundTaggedDigest}). That doc comment carries the field-vector rationale
|
|
168
|
+
* (notably why neither `strandId` nor the party id is signed); the two vectors must not
|
|
169
|
+
* drift, which peer-authorization.spec.ts pins by signing one form and verifying the other.
|
|
147
170
|
*/
|
|
148
171
|
export declare function formationConsentDigest(token: string, usageStampId: string, peerKey: string, disclosure: string): string;
|
|
149
172
|
/**
|
|
@@ -163,3 +186,75 @@ export declare function verifyFormationConsent(row: {
|
|
|
163
186
|
disclosure: string;
|
|
164
187
|
peerSig: string;
|
|
165
188
|
}): boolean;
|
|
189
|
+
/**
|
|
190
|
+
* Canonical digest an owner signs to seat a `CadreInvite` row — the read-side mirror
|
|
191
|
+
* of `cadreInviteAddMessage` in control-database.ts, base64url instead of raw bytes
|
|
192
|
+
* (see {@link taggedDigest}). The whole row is bound, nullable columns as `''` and
|
|
193
|
+
* `ExpiresAt` in its stored canonical form, through the one field builder both sides
|
|
194
|
+
* share (`cadreInviteRowFields`). SQL mirror: `CadreInvite.AuthorizedInsert`.
|
|
195
|
+
*/
|
|
196
|
+
export declare function cadreInviteAddDigest(partyId: string, row: CadreInviteSignedFields): string;
|
|
197
|
+
/**
|
|
198
|
+
* Canonical digest the HOLDER of a `CadreInvite`'s private key signs to redeem it once:
|
|
199
|
+
* proof of possession, verified against the invitation key itself
|
|
200
|
+
* (`CadreInviteUsage.InvitePossessed`). Binds the redemption's single-use nonce and the
|
|
201
|
+
* device being admitted, so one redemption cannot be re-presented for another device
|
|
202
|
+
* or another use. Mirror of `cadreInviteRedeemMessage` in control-database.ts.
|
|
203
|
+
*/
|
|
204
|
+
export declare function cadreInviteRedeemDigest(partyId: string, inviteKey: string, usageStampId: string, peerKey: string): string;
|
|
205
|
+
/**
|
|
206
|
+
* Canonical digest the DEVICE signs to consent to being admitted by one redemption
|
|
207
|
+
* (`CadreInviteUsage.PeerConsented`), over the same fields as
|
|
208
|
+
* {@link cadreInviteRedeemDigest} under a distinct action tag, so the holder's and
|
|
209
|
+
* the device's signatures are never interchangeable. Mirror of
|
|
210
|
+
* `cadreInviteConsentMessage` in control-database.ts.
|
|
211
|
+
*/
|
|
212
|
+
export declare function cadreInviteConsentDigest(partyId: string, inviteKey: string, usageStampId: string, peerKey: string): string;
|
|
213
|
+
/**
|
|
214
|
+
* Does a `CadreInvite` row carry a valid `'add'` signature by its own `issuerKey`, for
|
|
215
|
+
* `partyId`? The redeeming device runs this over the row a member replies with, after
|
|
216
|
+
* checking the issuer against its pinned owner keys and the reply's party against the
|
|
217
|
+
* invitation's, and a member over a bundle's copy it does not hold yet
|
|
218
|
+
* (`ControlDatabase.isCadreInviteLive`); whether that issuer IS an owner is the caller's question.
|
|
219
|
+
* Never throws: a malformed row verifies as `false`.
|
|
220
|
+
*/
|
|
221
|
+
export declare function verifyCadreInviteRow(partyId: string, invite: CadreInviteRow): boolean;
|
|
222
|
+
/**
|
|
223
|
+
* The two signatures a redemption carries, checked before any database work: the holder's
|
|
224
|
+
* `'redeem'` signature with the invitation key, and the device's `'consent'` signature with
|
|
225
|
+
* its own key, both over (`inviteKey`, `usageStampId`, `peerKey`). The pre-check twin of
|
|
226
|
+
* `CadreInviteUsage.InvitePossessed` / `PeerConsented`, as `verifyFormationConsent` is for
|
|
227
|
+
* formation: the schema stays the authority, this turns a bad signature into a clean
|
|
228
|
+
* refusal instead of a constraint failure at commit. Never throws.
|
|
229
|
+
*/
|
|
230
|
+
export declare function verifyCadreInviteRedemption(partyId: string, fields: {
|
|
231
|
+
inviteKey: string;
|
|
232
|
+
usageStampId: string;
|
|
233
|
+
peerKey: string;
|
|
234
|
+
}, inviteSig: string, peerSig: string): boolean;
|
|
235
|
+
/**
|
|
236
|
+
* Is an invitation-admitted `CadrePeer` row (`vouchSig` null, `vouchUsage` set) a
|
|
237
|
+
* member this node should trust? The read-side mirror of the consent branch of
|
|
238
|
+
* `CadrePeer.AuthorizedInsert`, re-checked against THIS node's anchor because the
|
|
239
|
+
* replicated `OwnerKey` table can be polluted. The usage must name this peer id and this
|
|
240
|
+
* exact row incarnation (`peerStampId`); every other link is {@link verifyAdmissionChain}.
|
|
241
|
+
*
|
|
242
|
+
* Expiry, use count and withdrawal are deliberately NOT re-checked: they were conditions
|
|
243
|
+
* at redemption (`CadreInviteUsage.Authorized`), and membership persists until an owner
|
|
244
|
+
* removes the row — exactly as an owner-vouched row's membership does. Keeping a device
|
|
245
|
+
* out is removing its row AND withdrawing the invitation.
|
|
246
|
+
*
|
|
247
|
+
* Returns a boolean and never throws (the siblings' contract): malformed input or any
|
|
248
|
+
* crypto failure resolves to `false`, logged at debug.
|
|
249
|
+
*/
|
|
250
|
+
export declare function verifyInvitationAdmission(partyId: string, row: CadrePeerVoucherFields, usage: CadreInviteUsageRow, invite: CadreInviteRow, isAnchored: (ownerKey: string) => boolean): boolean;
|
|
251
|
+
/**
|
|
252
|
+
* Is an invitation-admitted `OwnerKey` row (`vouchSig` null, `vouchUsage` set) an owner
|
|
253
|
+
* this node should derive into its anchor? The read-side mirror of the consent branch of
|
|
254
|
+
* `OwnerKey.Authorized` (`owner-anchor-sync.ts` → `deriveOwnerAnchor`): the invitation
|
|
255
|
+
* must grant ownership, and the usage must name this key (`peerKey`) and this exact row
|
|
256
|
+
* incarnation (`ownerStampId`); every other link is {@link verifyAdmissionChain}. Same
|
|
257
|
+
* never-throws contract and the same deliberate omissions as
|
|
258
|
+
* {@link verifyInvitationAdmission}.
|
|
259
|
+
*/
|
|
260
|
+
export declare function verifyInvitationOwnerAdmission(partyId: string, row: OwnerKeyRow, usage: CadreInviteUsageRow, invite: CadreInviteRow, isAnchored: (ownerKey: string) => boolean): boolean;
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import debug from 'debug';
|
|
2
2
|
import { digest, verify } from '@optimystic/quereus-plugin-crypto';
|
|
3
|
-
import { controlAuthorizationFields } from './control-authorization.js';
|
|
3
|
+
import { controlAuthorizationFields, unboundAuthorizationFields, cadreInviteRowFields } from './control-authorization.js';
|
|
4
|
+
import { ed25519PublicKeyB64FromPeerId } from './ed25519-key.js';
|
|
4
5
|
const log = debug('sereus:cadre:peer-authorization');
|
|
5
6
|
/**
|
|
6
|
-
* base64url SHA-256 digest over the shared domain-tagged field vector (see
|
|
7
|
+
* base64url SHA-256 digest over the shared domain-tagged, party-bound field vector (see
|
|
7
8
|
* control-authorization.ts). The base64url twin of
|
|
8
9
|
* control-database.ts:buildAuthorizationMessage (which returns the same digest as raw
|
|
9
10
|
* bytes): sign either encoding with input encoding to match and the signed bytes agree.
|
|
11
|
+
* `partyId` is the party the verifier is configured for: a stored proof minted for
|
|
12
|
+
* another party fails here exactly as it fails the schema's `party_id()`-bound check.
|
|
10
13
|
*/
|
|
11
|
-
function taggedDigest(domain, action, rowFields) {
|
|
12
|
-
return digest(controlAuthorizationFields(domain, action, rowFields), 'sha256', 'base64url');
|
|
14
|
+
function taggedDigest(domain, action, partyId, rowFields) {
|
|
15
|
+
return digest(controlAuthorizationFields(domain, action, partyId, rowFields), 'sha256', 'base64url');
|
|
16
|
+
}
|
|
17
|
+
/** {@link taggedDigest} for the two party-free digests (`UnboundControlDomain`). */
|
|
18
|
+
function unboundTaggedDigest(domain, action, rowFields) {
|
|
19
|
+
return digest(unboundAuthorizationFields(domain, action, rowFields), 'sha256', 'base64url');
|
|
13
20
|
}
|
|
14
21
|
/**
|
|
15
22
|
* Canonical digest an owner signs to vouch a peer's ENROLLMENT — the offline
|
|
@@ -23,12 +30,12 @@ function taggedDigest(domain, action, rowFields) {
|
|
|
23
30
|
* here and both move together.
|
|
24
31
|
*/
|
|
25
32
|
export function peerAuthorizationDigest(peerId) {
|
|
26
|
-
return
|
|
33
|
+
return unboundTaggedDigest('Cadre.Enrollment', 'vouch', [peerId]);
|
|
27
34
|
}
|
|
28
35
|
/**
|
|
29
36
|
* Canonical digest an owner signs to authorize a `DeviceToken` INSERT — the WHOLE row,
|
|
30
37
|
* ending in its single-use `StampId` nonce. SQL mirror:
|
|
31
|
-
* `digest('CadreControl.DeviceToken', 'add', new.PeerId, new.Platform, new.Token,
|
|
38
|
+
* `digest('CadreControl.DeviceToken', 'add', party_id(), new.PeerId, new.Platform, new.Token,
|
|
32
39
|
* coalesce(cast(new.UpdatedAt as text), ''), coalesce(new.Sig, ''), new.StampId)` in
|
|
33
40
|
* `DeviceToken.AuthorizedInsert`.
|
|
34
41
|
*
|
|
@@ -44,8 +51,8 @@ export function peerAuthorizationDigest(peerId) {
|
|
|
44
51
|
* Distinct from {@link deviceTokenRemoveDigest} so a captured insert approval can never
|
|
45
52
|
* be replayed to delete the token, and vice versa.
|
|
46
53
|
*/
|
|
47
|
-
export function deviceTokenAddDigest(row) {
|
|
48
|
-
return taggedDigest('CadreControl.DeviceToken', 'add', [
|
|
54
|
+
export function deviceTokenAddDigest(partyId, row) {
|
|
55
|
+
return taggedDigest('CadreControl.DeviceToken', 'add', partyId, [
|
|
49
56
|
row.peerId,
|
|
50
57
|
row.platform,
|
|
51
58
|
row.token,
|
|
@@ -57,7 +64,7 @@ export function deviceTokenAddDigest(row) {
|
|
|
57
64
|
/**
|
|
58
65
|
* Canonical digest an owner signs to authorize a `DeviceToken` DELETE, bound to the
|
|
59
66
|
* STORED row's (PeerId, StampId). SQL mirror:
|
|
60
|
-
* `digest('CadreControl.DeviceToken', 'remove', old.PeerId, old.StampId)` in
|
|
67
|
+
* `digest('CadreControl.DeviceToken', 'remove', party_id(), old.PeerId, old.StampId)` in
|
|
61
68
|
* `DeviceToken.AuthorizedDelete`.
|
|
62
69
|
*
|
|
63
70
|
* A narrower vector than {@link deviceTokenAddDigest} on purpose (the same split
|
|
@@ -65,8 +72,8 @@ export function deviceTokenAddDigest(row) {
|
|
|
65
72
|
* cannot be re-cut into an insert approval, and it is dead the moment the stamp it
|
|
66
73
|
* names is tombstoned.
|
|
67
74
|
*/
|
|
68
|
-
export function deviceTokenRemoveDigest(peerId, stampId) {
|
|
69
|
-
return taggedDigest('CadreControl.DeviceToken', 'remove', [peerId, stampId]);
|
|
75
|
+
export function deviceTokenRemoveDigest(partyId, peerId, stampId) {
|
|
76
|
+
return taggedDigest('CadreControl.DeviceToken', 'remove', partyId, [peerId, stampId]);
|
|
70
77
|
}
|
|
71
78
|
/**
|
|
72
79
|
* Canonical digest an owner signs to VOUCH a `CadrePeer` membership row (insert
|
|
@@ -79,10 +86,10 @@ export function deviceTokenRemoveDigest(peerId, stampId) {
|
|
|
79
86
|
* a delete. The domain tag keeps the stored, replicated `VouchSig` useless against
|
|
80
87
|
* every OTHER table's rules.
|
|
81
88
|
*
|
|
82
|
-
* SQL mirror: `digest('CadreControl.CadrePeer', 'vouch', new.PeerId, new.StampId)`.
|
|
89
|
+
* SQL mirror: `digest('CadreControl.CadrePeer', 'vouch', party_id(), new.PeerId, new.StampId)`.
|
|
83
90
|
*/
|
|
84
|
-
export function cadrePeerVoucherDigest(peerId, stampId) {
|
|
85
|
-
return taggedDigest('CadreControl.CadrePeer', 'vouch', [peerId, stampId]);
|
|
91
|
+
export function cadrePeerVoucherDigest(partyId, peerId, stampId) {
|
|
92
|
+
return taggedDigest('CadreControl.CadrePeer', 'vouch', partyId, [peerId, stampId]);
|
|
86
93
|
}
|
|
87
94
|
/**
|
|
88
95
|
* Canonical digest an owner signs to REMOVE a `CadrePeer` row. Deliberately a
|
|
@@ -93,10 +100,10 @@ export function cadrePeerVoucherDigest(peerId, stampId) {
|
|
|
93
100
|
* delete lands, because a re-added row carries a FRESH `StampId` (the removed row's
|
|
94
101
|
* stamp is retired into `CadreControl.Revocation` and never reused).
|
|
95
102
|
*
|
|
96
|
-
* SQL mirror: `digest('CadreControl.CadrePeer', 'remove', old.PeerId, old.StampId)`.
|
|
103
|
+
* SQL mirror: `digest('CadreControl.CadrePeer', 'remove', party_id(), old.PeerId, old.StampId)`.
|
|
97
104
|
*/
|
|
98
|
-
export function cadrePeerRemoveDigest(peerId, stampId) {
|
|
99
|
-
return taggedDigest('CadreControl.CadrePeer', 'remove', [peerId, stampId]);
|
|
105
|
+
export function cadrePeerRemoveDigest(partyId, peerId, stampId) {
|
|
106
|
+
return taggedDigest('CadreControl.CadrePeer', 'remove', partyId, [peerId, stampId]);
|
|
100
107
|
}
|
|
101
108
|
/**
|
|
102
109
|
* Canonical digest an owner signs to APPEND a `CadreControl.Revocation` tombstone —
|
|
@@ -104,7 +111,7 @@ export function cadrePeerRemoveDigest(peerId, stampId) {
|
|
|
104
111
|
* (the removed row's primary key: OwnerKey.Key / ValidationKey.Key /
|
|
105
112
|
* CadrePeer.PeerId / DeviceToken.PeerId / Strand.Id) as which row was retired.
|
|
106
113
|
* SQL mirror:
|
|
107
|
-
* `digest('CadreControl.Revocation', 'remove', new.TableName, new.RowKey, new.StampId)`
|
|
114
|
+
* `digest('CadreControl.Revocation', 'remove', party_id(), new.TableName, new.RowKey, new.StampId)`
|
|
108
115
|
* in `Revocation.Authorized`.
|
|
109
116
|
*
|
|
110
117
|
* Its own `'CadreControl.Revocation'` domain tag makes it disjoint from the
|
|
@@ -113,8 +120,47 @@ export function cadrePeerRemoveDigest(peerId, stampId) {
|
|
|
113
120
|
* this tombstone accompanies — a removal signature is not a retirement
|
|
114
121
|
* signature and cannot be replayed as one.
|
|
115
122
|
*/
|
|
116
|
-
export function revocationDigest(tableName, rowKey, stampId) {
|
|
117
|
-
return taggedDigest('CadreControl.Revocation', 'remove', [tableName, rowKey, stampId]);
|
|
123
|
+
export function revocationDigest(partyId, tableName, rowKey, stampId) {
|
|
124
|
+
return taggedDigest('CadreControl.Revocation', 'remove', partyId, [tableName, rowKey, stampId]);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Does a `Revocation` row's stored signer pair (`SignerKey` / `SignerSig`) verify over the
|
|
128
|
+
* tombstone's own triple, for `partyId`? The read-side mirror of `Revocation.Authorized`,
|
|
129
|
+
* for a node that received the row by replication and must decide whether to honour it.
|
|
130
|
+
* Whether `row.signerKey` is an owner this node trusts is the caller's question, as for
|
|
131
|
+
* {@link verifyCadrePeerVoucher}. Never throws: a malformed row verifies as `false`.
|
|
132
|
+
*/
|
|
133
|
+
export function verifyRevocationSigner(partyId, row) {
|
|
134
|
+
try {
|
|
135
|
+
return verifyB64(revocationDigest(partyId, row.tableName, row.rowKey, row.stampId), row.signerSig, row.signerKey);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
log('verifyRevocationSigner failed: %o', error);
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Canonical digest an owner signs to seat a further `OwnerKey` row — the signed-add branch
|
|
144
|
+
* of `OwnerKey.Authorized`, which stores the pair as `VouchOwner` / `VouchSig`. SQL mirror:
|
|
145
|
+
* `digest('CadreControl.OwnerKey', 'add', party_id(), new.Key, new.StampId)`. The removal
|
|
146
|
+
* digest needs no builder here: it is the generic guarded `'remove'` digest over (Key, StampId).
|
|
147
|
+
*/
|
|
148
|
+
export function ownerKeyAddDigest(partyId, key, stampId) {
|
|
149
|
+
return taggedDigest('CadreControl.OwnerKey', 'add', partyId, [key, stampId]);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Does an owner-signed `OwnerKey` row's stored voucher verify — `vouchSig` by `vouchOwner`
|
|
153
|
+
* over {@link ownerKeyAddDigest} for (`partyId`, `key`, `stampId`)? Whether `vouchOwner` is
|
|
154
|
+
* anchored is the caller's question, as for {@link verifyCadrePeerVoucher}. Never throws.
|
|
155
|
+
*/
|
|
156
|
+
export function verifyOwnerKeyVoucher(partyId, key, stampId, vouchOwner, vouchSig) {
|
|
157
|
+
try {
|
|
158
|
+
return verifyB64(ownerKeyAddDigest(partyId, key, stampId), vouchSig, vouchOwner);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
log('verifyOwnerKeyVoucher failed: %o', error);
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
118
164
|
}
|
|
119
165
|
/**
|
|
120
166
|
* Verify that `signature` is a valid owner ed25519 signature over `peerId`'s
|
|
@@ -141,12 +187,13 @@ export function verifyPeerAuthorization(peerId, ownerPublicKey, signature) {
|
|
|
141
187
|
}
|
|
142
188
|
/**
|
|
143
189
|
* Verify that `signature` is a valid owner ed25519 signature over the
|
|
144
|
-
* `CadrePeer` voucher digest for (`peerId`, `stampId`) — the read-side
|
|
145
|
-
* of the voucher {@link ControlDatabase.insertCadrePeer} signs and persists into
|
|
190
|
+
* `CadrePeer` voucher digest for (`partyId`, `peerId`, `stampId`) — the read-side
|
|
191
|
+
* mirror of the voucher {@link ControlDatabase.insertCadrePeer} signs and persists into
|
|
146
192
|
* `VouchOwner`/`VouchSig` (see {@link cadrePeerVoucherDigest}).
|
|
147
193
|
*
|
|
148
194
|
* A `true` result means the holder of `ownerPublicKey` vouched THIS membership
|
|
149
|
-
* row (the peer id bound to the row's single-use `StampId` nonce)
|
|
195
|
+
* row (the peer id bound to the row's single-use `StampId` nonce) in THIS party: a
|
|
196
|
+
* voucher copied from another party's table that shares the owner key fails. It says
|
|
150
197
|
* nothing about whether that owner key is itself trustworthy — the caller must
|
|
151
198
|
* separately check the key against the node-local trusted-owner anchor
|
|
152
199
|
* (`TrustedOwnerStore`), never the replicated `OwnerKey` table.
|
|
@@ -155,9 +202,9 @@ export function verifyPeerAuthorization(peerId, ownerPublicKey, signature) {
|
|
|
155
202
|
* {@link verifyPeerAuthorization}): malformed input or any crypto failure
|
|
156
203
|
* resolves to `false`, logged at debug.
|
|
157
204
|
*/
|
|
158
|
-
export function verifyCadrePeerVoucher(peerId, stampId, ownerPublicKey, signature) {
|
|
205
|
+
export function verifyCadrePeerVoucher(partyId, peerId, stampId, ownerPublicKey, signature) {
|
|
159
206
|
try {
|
|
160
|
-
return verify(cadrePeerVoucherDigest(peerId, stampId), signature, ownerPublicKey, 'ed25519', 'base64url', 'base64url', 'base64url');
|
|
207
|
+
return verify(cadrePeerVoucherDigest(partyId, peerId, stampId), signature, ownerPublicKey, 'ed25519', 'base64url', 'base64url', 'base64url');
|
|
161
208
|
}
|
|
162
209
|
catch (error) {
|
|
163
210
|
log('verifyCadrePeerVoucher failed: %o', error);
|
|
@@ -168,12 +215,12 @@ export function verifyCadrePeerVoucher(peerId, stampId, ownerPublicKey, signatur
|
|
|
168
215
|
* Canonical digest a JOINING peer signs to consent to ONE `FormationUsage`
|
|
169
216
|
* redemption — the read-side mirror of `formationConsentMessage` in
|
|
170
217
|
* control-database.ts, base64url-encoded instead of raw bytes (see
|
|
171
|
-
* {@link
|
|
172
|
-
* (notably why `strandId` is
|
|
173
|
-
* peer-authorization.spec.ts pins by signing one form and verifying the other.
|
|
218
|
+
* {@link unboundTaggedDigest}). That doc comment carries the field-vector rationale
|
|
219
|
+
* (notably why neither `strandId` nor the party id is signed); the two vectors must not
|
|
220
|
+
* drift, which peer-authorization.spec.ts pins by signing one form and verifying the other.
|
|
174
221
|
*/
|
|
175
222
|
export function formationConsentDigest(token, usageStampId, peerKey, disclosure) {
|
|
176
|
-
return
|
|
223
|
+
return unboundTaggedDigest('CadreControl.FormationUsage', 'consent', [token, usageStampId, peerKey, disclosure]);
|
|
177
224
|
}
|
|
178
225
|
/**
|
|
179
226
|
* Verify that `row.peerSig` is a valid ed25519 signature over the joining peer's
|
|
@@ -194,4 +241,153 @@ export function verifyFormationConsent(row) {
|
|
|
194
241
|
return false;
|
|
195
242
|
}
|
|
196
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Canonical digest an owner signs to seat a `CadreInvite` row — the read-side mirror
|
|
246
|
+
* of `cadreInviteAddMessage` in control-database.ts, base64url instead of raw bytes
|
|
247
|
+
* (see {@link taggedDigest}). The whole row is bound, nullable columns as `''` and
|
|
248
|
+
* `ExpiresAt` in its stored canonical form, through the one field builder both sides
|
|
249
|
+
* share (`cadreInviteRowFields`). SQL mirror: `CadreInvite.AuthorizedInsert`.
|
|
250
|
+
*/
|
|
251
|
+
export function cadreInviteAddDigest(partyId, row) {
|
|
252
|
+
return taggedDigest('CadreControl.CadreInvite', 'add', partyId, cadreInviteRowFields(row));
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Canonical digest the HOLDER of a `CadreInvite`'s private key signs to redeem it once:
|
|
256
|
+
* proof of possession, verified against the invitation key itself
|
|
257
|
+
* (`CadreInviteUsage.InvitePossessed`). Binds the redemption's single-use nonce and the
|
|
258
|
+
* device being admitted, so one redemption cannot be re-presented for another device
|
|
259
|
+
* or another use. Mirror of `cadreInviteRedeemMessage` in control-database.ts.
|
|
260
|
+
*/
|
|
261
|
+
export function cadreInviteRedeemDigest(partyId, inviteKey, usageStampId, peerKey) {
|
|
262
|
+
return taggedDigest('CadreControl.CadreInviteUsage', 'redeem', partyId, [inviteKey, usageStampId, peerKey]);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Canonical digest the DEVICE signs to consent to being admitted by one redemption
|
|
266
|
+
* (`CadreInviteUsage.PeerConsented`), over the same fields as
|
|
267
|
+
* {@link cadreInviteRedeemDigest} under a distinct action tag, so the holder's and
|
|
268
|
+
* the device's signatures are never interchangeable. Mirror of
|
|
269
|
+
* `cadreInviteConsentMessage` in control-database.ts.
|
|
270
|
+
*/
|
|
271
|
+
export function cadreInviteConsentDigest(partyId, inviteKey, usageStampId, peerKey) {
|
|
272
|
+
return taggedDigest('CadreControl.CadreInviteUsage', 'consent', partyId, [inviteKey, usageStampId, peerKey]);
|
|
273
|
+
}
|
|
274
|
+
/** ed25519 verify over a base64url digest, with the siblings' never-throws contract left to the caller. */
|
|
275
|
+
function verifyB64(digestB64, signature, publicKey) {
|
|
276
|
+
return verify(digestB64, signature, publicKey, 'ed25519', 'base64url', 'base64url', 'base64url');
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Does a `CadreInvite` row carry a valid `'add'` signature by its own `issuerKey`, for
|
|
280
|
+
* `partyId`? The redeeming device runs this over the row a member replies with, after
|
|
281
|
+
* checking the issuer against its pinned owner keys and the reply's party against the
|
|
282
|
+
* invitation's, and a member over a bundle's copy it does not hold yet
|
|
283
|
+
* (`ControlDatabase.isCadreInviteLive`); whether that issuer IS an owner is the caller's question.
|
|
284
|
+
* Never throws: a malformed row verifies as `false`.
|
|
285
|
+
*/
|
|
286
|
+
export function verifyCadreInviteRow(partyId, invite) {
|
|
287
|
+
try {
|
|
288
|
+
return verifyB64(cadreInviteAddDigest(partyId, invite), invite.issuerSig, invite.issuerKey);
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
log('verifyCadreInviteRow failed: %o', error);
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* The two signatures a redemption carries, checked before any database work: the holder's
|
|
297
|
+
* `'redeem'` signature with the invitation key, and the device's `'consent'` signature with
|
|
298
|
+
* its own key, both over (`inviteKey`, `usageStampId`, `peerKey`). The pre-check twin of
|
|
299
|
+
* `CadreInviteUsage.InvitePossessed` / `PeerConsented`, as `verifyFormationConsent` is for
|
|
300
|
+
* formation: the schema stays the authority, this turns a bad signature into a clean
|
|
301
|
+
* refusal instead of a constraint failure at commit. Never throws.
|
|
302
|
+
*/
|
|
303
|
+
export function verifyCadreInviteRedemption(partyId, fields, inviteSig, peerSig) {
|
|
304
|
+
try {
|
|
305
|
+
return verifyB64(cadreInviteRedeemDigest(partyId, fields.inviteKey, fields.usageStampId, fields.peerKey), inviteSig, fields.inviteKey)
|
|
306
|
+
&& verifyB64(cadreInviteConsentDigest(partyId, fields.inviteKey, fields.usageStampId, fields.peerKey), peerSig, fields.peerKey);
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
log('verifyCadreInviteRedemption failed: %o', error);
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* The links of an invitation admission that do not depend on WHICH table's row was
|
|
315
|
+
* admitted — shared by {@link verifyInvitationAdmission} (`CadrePeer`) and
|
|
316
|
+
* {@link verifyInvitationOwnerAdmission} (`OwnerKey`), which differ only in the row
|
|
317
|
+
* columns the usage must name and, for an owner row, in `grantsOwner`. The chain is
|
|
318
|
+
*
|
|
319
|
+
* row --vouchUsage--> usage --inviteKey--> invitation --issuerKey--> anchored owner
|
|
320
|
+
*
|
|
321
|
+
* and every link here is verified: the issuer is anchored (`isAnchored`) and is the row's
|
|
322
|
+
* `vouchOwner`; the usage names this invitation and is the row's `vouchUsage`; a targeted
|
|
323
|
+
* invitation names the redeeming device; the stored `peerKey` really is the key behind the
|
|
324
|
+
* usage's `peerId` (the schema cannot unwrap a multihash, so a writer could assert any
|
|
325
|
+
* pair); the invitation's stored `'add'` signature verifies over the row rebuilt from its
|
|
326
|
+
* columns; the holder's `'redeem'` signature verifies with the invitation key and the
|
|
327
|
+
* device's `'consent'` signature with its own key. All three signatures are judged for
|
|
328
|
+
* `partyId`. Throws on malformed input; the two callers turn that into `false`.
|
|
329
|
+
*/
|
|
330
|
+
function verifyAdmissionChain(partyId, vouchOwner, vouchUsage, usage, invite, isAnchored) {
|
|
331
|
+
return isAnchored(vouchOwner)
|
|
332
|
+
&& invite.issuerKey === vouchOwner
|
|
333
|
+
&& usage.usageStampId === vouchUsage
|
|
334
|
+
&& usage.inviteKey === invite.key
|
|
335
|
+
&& (invite.peerId === null || invite.peerId === usage.peerId)
|
|
336
|
+
&& ed25519PublicKeyB64FromPeerId(usage.peerId) === usage.peerKey
|
|
337
|
+
&& verifyB64(cadreInviteAddDigest(partyId, invite), invite.issuerSig, invite.issuerKey)
|
|
338
|
+
&& verifyB64(cadreInviteRedeemDigest(partyId, invite.key, usage.usageStampId, usage.peerKey), usage.inviteSig, invite.key)
|
|
339
|
+
&& verifyB64(cadreInviteConsentDigest(partyId, invite.key, usage.usageStampId, usage.peerKey), usage.peerSig, usage.peerKey);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Is an invitation-admitted `CadrePeer` row (`vouchSig` null, `vouchUsage` set) a
|
|
343
|
+
* member this node should trust? The read-side mirror of the consent branch of
|
|
344
|
+
* `CadrePeer.AuthorizedInsert`, re-checked against THIS node's anchor because the
|
|
345
|
+
* replicated `OwnerKey` table can be polluted. The usage must name this peer id and this
|
|
346
|
+
* exact row incarnation (`peerStampId`); every other link is {@link verifyAdmissionChain}.
|
|
347
|
+
*
|
|
348
|
+
* Expiry, use count and withdrawal are deliberately NOT re-checked: they were conditions
|
|
349
|
+
* at redemption (`CadreInviteUsage.Authorized`), and membership persists until an owner
|
|
350
|
+
* removes the row — exactly as an owner-vouched row's membership does. Keeping a device
|
|
351
|
+
* out is removing its row AND withdrawing the invitation.
|
|
352
|
+
*
|
|
353
|
+
* Returns a boolean and never throws (the siblings' contract): malformed input or any
|
|
354
|
+
* crypto failure resolves to `false`, logged at debug.
|
|
355
|
+
*/
|
|
356
|
+
export function verifyInvitationAdmission(partyId, row, usage, invite, isAnchored) {
|
|
357
|
+
try {
|
|
358
|
+
return row.stampId !== null
|
|
359
|
+
&& row.vouchOwner !== null
|
|
360
|
+
&& row.vouchUsage !== null
|
|
361
|
+
&& usage.peerStampId === row.stampId
|
|
362
|
+
&& usage.peerId === row.peerId
|
|
363
|
+
&& verifyAdmissionChain(partyId, row.vouchOwner, row.vouchUsage, usage, invite, isAnchored);
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
log('verifyInvitationAdmission failed: %o', error);
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Is an invitation-admitted `OwnerKey` row (`vouchSig` null, `vouchUsage` set) an owner
|
|
372
|
+
* this node should derive into its anchor? The read-side mirror of the consent branch of
|
|
373
|
+
* `OwnerKey.Authorized` (`owner-anchor-sync.ts` → `deriveOwnerAnchor`): the invitation
|
|
374
|
+
* must grant ownership, and the usage must name this key (`peerKey`) and this exact row
|
|
375
|
+
* incarnation (`ownerStampId`); every other link is {@link verifyAdmissionChain}. Same
|
|
376
|
+
* never-throws contract and the same deliberate omissions as
|
|
377
|
+
* {@link verifyInvitationAdmission}.
|
|
378
|
+
*/
|
|
379
|
+
export function verifyInvitationOwnerAdmission(partyId, row, usage, invite, isAnchored) {
|
|
380
|
+
try {
|
|
381
|
+
return row.vouchOwner !== null
|
|
382
|
+
&& row.vouchUsage !== null
|
|
383
|
+
&& invite.grantsOwner
|
|
384
|
+
&& usage.peerKey === row.key
|
|
385
|
+
&& usage.ownerStampId === row.stampId
|
|
386
|
+
&& verifyAdmissionChain(partyId, row.vouchOwner, row.vouchUsage, usage, invite, isAnchored);
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
log('verifyInvitationOwnerAdmission failed: %o', error);
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
197
393
|
//# sourceMappingURL=peer-authorization.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peer-authorization.js","sourceRoot":"","sources":["../src/peer-authorization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"peer-authorization.js","sourceRoot":"","sources":["../src/peer-authorization.ts"],"names":[],"mappings":"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"}
|