agent-passport-system 7.0.0 → 7.2.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/SECURITY.md +11 -0
- package/dist/src/core/tool-integrity.d.ts +13 -0
- package/dist/src/core/tool-integrity.d.ts.map +1 -1
- package/dist/src/core/tool-integrity.js +1 -1
- package/dist/src/core/tool-integrity.js.map +1 -1
- package/dist/src/index.d.ts +52 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +282 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/v2/activation/canonical.d.ts +40 -0
- package/dist/src/v2/activation/canonical.d.ts.map +1 -0
- package/dist/src/v2/activation/canonical.js +64 -0
- package/dist/src/v2/activation/canonical.js.map +1 -0
- package/dist/src/v2/activation/index.d.ts +4 -0
- package/dist/src/v2/activation/index.d.ts.map +1 -0
- package/dist/src/v2/activation/index.js +11 -0
- package/dist/src/v2/activation/index.js.map +1 -0
- package/dist/src/v2/activation/types.d.ts +268 -0
- package/dist/src/v2/activation/types.d.ts.map +1 -0
- package/dist/src/v2/activation/types.js +198 -0
- package/dist/src/v2/activation/types.js.map +1 -0
- package/dist/src/v2/activation/verify.d.ts +132 -0
- package/dist/src/v2/activation/verify.d.ts.map +1 -0
- package/dist/src/v2/activation/verify.js +406 -0
- package/dist/src/v2/activation/verify.js.map +1 -0
- package/dist/src/v2/authority-revocation/canonical.d.ts +46 -0
- package/dist/src/v2/authority-revocation/canonical.d.ts.map +1 -0
- package/dist/src/v2/authority-revocation/canonical.js +68 -0
- package/dist/src/v2/authority-revocation/canonical.js.map +1 -0
- package/dist/src/v2/authority-revocation/index.d.ts +9 -0
- package/dist/src/v2/authority-revocation/index.d.ts.map +1 -0
- package/dist/src/v2/authority-revocation/index.js +11 -0
- package/dist/src/v2/authority-revocation/index.js.map +1 -0
- package/dist/src/v2/authority-revocation/issue.d.ts +54 -0
- package/dist/src/v2/authority-revocation/issue.d.ts.map +1 -0
- package/dist/src/v2/authority-revocation/issue.js +111 -0
- package/dist/src/v2/authority-revocation/issue.js.map +1 -0
- package/dist/src/v2/authority-revocation/record.d.ts +69 -0
- package/dist/src/v2/authority-revocation/record.d.ts.map +1 -0
- package/dist/src/v2/authority-revocation/record.js +51 -0
- package/dist/src/v2/authority-revocation/record.js.map +1 -0
- package/dist/src/v2/authority-revocation/resolver.d.ts +30 -0
- package/dist/src/v2/authority-revocation/resolver.d.ts.map +1 -0
- package/dist/src/v2/authority-revocation/resolver.js +52 -0
- package/dist/src/v2/authority-revocation/resolver.js.map +1 -0
- package/dist/src/v2/authority-revocation/schema.d.ts +15 -0
- package/dist/src/v2/authority-revocation/schema.d.ts.map +1 -0
- package/dist/src/v2/authority-revocation/schema.js +107 -0
- package/dist/src/v2/authority-revocation/schema.js.map +1 -0
- package/dist/src/v2/authority-revocation/store.d.ts +55 -0
- package/dist/src/v2/authority-revocation/store.d.ts.map +1 -0
- package/dist/src/v2/authority-revocation/store.js +69 -0
- package/dist/src/v2/authority-revocation/store.js.map +1 -0
- package/dist/src/v2/authority-revocation/types.d.ts +136 -0
- package/dist/src/v2/authority-revocation/types.d.ts.map +1 -0
- package/dist/src/v2/authority-revocation/types.js +11 -0
- package/dist/src/v2/authority-revocation/types.js.map +1 -0
- package/dist/src/v2/authority-revocation/verify.d.ts +38 -0
- package/dist/src/v2/authority-revocation/verify.d.ts.map +1 -0
- package/dist/src/v2/authority-revocation/verify.js +139 -0
- package/dist/src/v2/authority-revocation/verify.js.map +1 -0
- package/dist/src/v2/authority-state/fencing.d.ts +49 -0
- package/dist/src/v2/authority-state/fencing.d.ts.map +1 -0
- package/dist/src/v2/authority-state/fencing.js +92 -0
- package/dist/src/v2/authority-state/fencing.js.map +1 -0
- package/dist/src/v2/authority-state/index.d.ts +7 -0
- package/dist/src/v2/authority-state/index.d.ts.map +1 -0
- package/dist/src/v2/authority-state/index.js +14 -0
- package/dist/src/v2/authority-state/index.js.map +1 -0
- package/dist/src/v2/authority-state/marker.d.ts +70 -0
- package/dist/src/v2/authority-state/marker.d.ts.map +1 -0
- package/dist/src/v2/authority-state/marker.js +122 -0
- package/dist/src/v2/authority-state/marker.js.map +1 -0
- package/dist/src/v2/authority-state/report.d.ts +55 -0
- package/dist/src/v2/authority-state/report.d.ts.map +1 -0
- package/dist/src/v2/authority-state/report.js +51 -0
- package/dist/src/v2/authority-state/report.js.map +1 -0
- package/dist/src/v2/authority-state/retained.d.ts +82 -0
- package/dist/src/v2/authority-state/retained.d.ts.map +1 -0
- package/dist/src/v2/authority-state/retained.js +93 -0
- package/dist/src/v2/authority-state/retained.js.map +1 -0
- package/dist/src/v2/authority-state/types.d.ts +198 -0
- package/dist/src/v2/authority-state/types.d.ts.map +1 -0
- package/dist/src/v2/authority-state/types.js +103 -0
- package/dist/src/v2/authority-state/types.js.map +1 -0
- package/dist/src/v2/authority-state/withdrawal.d.ts +80 -0
- package/dist/src/v2/authority-state/withdrawal.d.ts.map +1 -0
- package/dist/src/v2/authority-state/withdrawal.js +199 -0
- package/dist/src/v2/authority-state/withdrawal.js.map +1 -0
- package/dist/src/v2/bounds/canonical.d.ts +30 -0
- package/dist/src/v2/bounds/canonical.d.ts.map +1 -0
- package/dist/src/v2/bounds/canonical.js +69 -0
- package/dist/src/v2/bounds/canonical.js.map +1 -0
- package/dist/src/v2/bounds/evaluate.d.ts +107 -0
- package/dist/src/v2/bounds/evaluate.d.ts.map +1 -0
- package/dist/src/v2/bounds/evaluate.js +315 -0
- package/dist/src/v2/bounds/evaluate.js.map +1 -0
- package/dist/src/v2/bounds/index.d.ts +6 -0
- package/dist/src/v2/bounds/index.d.ts.map +1 -0
- package/dist/src/v2/bounds/index.js +13 -0
- package/dist/src/v2/bounds/index.js.map +1 -0
- package/dist/src/v2/bounds/purpose.d.ts +2 -0
- package/dist/src/v2/bounds/purpose.d.ts.map +1 -0
- package/dist/src/v2/bounds/purpose.js +23 -0
- package/dist/src/v2/bounds/purpose.js.map +1 -0
- package/dist/src/v2/bounds/record.d.ts +78 -0
- package/dist/src/v2/bounds/record.d.ts.map +1 -0
- package/dist/src/v2/bounds/record.js +170 -0
- package/dist/src/v2/bounds/record.js.map +1 -0
- package/dist/src/v2/bounds/types.d.ts +275 -0
- package/dist/src/v2/bounds/types.d.ts.map +1 -0
- package/dist/src/v2/bounds/types.js +124 -0
- package/dist/src/v2/bounds/types.js.map +1 -0
- package/dist/src/v2/capability-binding/binding.d.ts +67 -0
- package/dist/src/v2/capability-binding/binding.d.ts.map +1 -0
- package/dist/src/v2/capability-binding/binding.js +267 -0
- package/dist/src/v2/capability-binding/binding.js.map +1 -0
- package/dist/src/v2/capability-binding/digest.d.ts +33 -0
- package/dist/src/v2/capability-binding/digest.d.ts.map +1 -0
- package/dist/src/v2/capability-binding/digest.js +55 -0
- package/dist/src/v2/capability-binding/digest.js.map +1 -0
- package/dist/src/v2/capability-binding/identifier.d.ts +103 -0
- package/dist/src/v2/capability-binding/identifier.d.ts.map +1 -0
- package/dist/src/v2/capability-binding/identifier.js +281 -0
- package/dist/src/v2/capability-binding/identifier.js.map +1 -0
- package/dist/src/v2/capability-binding/index.d.ts +6 -0
- package/dist/src/v2/capability-binding/index.d.ts.map +1 -0
- package/dist/src/v2/capability-binding/index.js +13 -0
- package/dist/src/v2/capability-binding/index.js.map +1 -0
- package/dist/src/v2/capability-binding/pins.d.ts +30 -0
- package/dist/src/v2/capability-binding/pins.d.ts.map +1 -0
- package/dist/src/v2/capability-binding/pins.js +109 -0
- package/dist/src/v2/capability-binding/pins.js.map +1 -0
- package/dist/src/v2/capability-binding/types.d.ts +213 -0
- package/dist/src/v2/capability-binding/types.d.ts.map +1 -0
- package/dist/src/v2/capability-binding/types.js +150 -0
- package/dist/src/v2/capability-binding/types.js.map +1 -0
- package/dist/src/v2/chain-selection/index.d.ts +4 -0
- package/dist/src/v2/chain-selection/index.d.ts.map +1 -0
- package/dist/src/v2/chain-selection/index.js +11 -0
- package/dist/src/v2/chain-selection/index.js.map +1 -0
- package/dist/src/v2/chain-selection/select.d.ts +62 -0
- package/dist/src/v2/chain-selection/select.d.ts.map +1 -0
- package/dist/src/v2/chain-selection/select.js +390 -0
- package/dist/src/v2/chain-selection/select.js.map +1 -0
- package/dist/src/v2/chain-selection/types.d.ts +189 -0
- package/dist/src/v2/chain-selection/types.d.ts.map +1 -0
- package/dist/src/v2/chain-selection/types.js +81 -0
- package/dist/src/v2/chain-selection/types.js.map +1 -0
- package/dist/src/v2/lifecycle-state/index.d.ts +4 -0
- package/dist/src/v2/lifecycle-state/index.d.ts.map +1 -0
- package/dist/src/v2/lifecycle-state/index.js +10 -0
- package/dist/src/v2/lifecycle-state/index.js.map +1 -0
- package/dist/src/v2/lifecycle-state/map.d.ts +52 -0
- package/dist/src/v2/lifecycle-state/map.d.ts.map +1 -0
- package/dist/src/v2/lifecycle-state/map.js +122 -0
- package/dist/src/v2/lifecycle-state/map.js.map +1 -0
- package/dist/src/v2/lifecycle-state/state.d.ts +60 -0
- package/dist/src/v2/lifecycle-state/state.d.ts.map +1 -0
- package/dist/src/v2/lifecycle-state/state.js +157 -0
- package/dist/src/v2/lifecycle-state/state.js.map +1 -0
- package/dist/src/v2/lifecycle-state/types.d.ts +163 -0
- package/dist/src/v2/lifecycle-state/types.d.ts.map +1 -0
- package/dist/src/v2/lifecycle-state/types.js +121 -0
- package/dist/src/v2/lifecycle-state/types.js.map +1 -0
- package/dist/src/v2/receipt-core/composite.d.ts +69 -3
- package/dist/src/v2/receipt-core/composite.d.ts.map +1 -1
- package/dist/src/v2/receipt-core/composite.js +75 -9
- package/dist/src/v2/receipt-core/composite.js.map +1 -1
- package/dist/src/v2/receipt-core/predecessor.d.ts +95 -0
- package/dist/src/v2/receipt-core/predecessor.d.ts.map +1 -0
- package/dist/src/v2/receipt-core/predecessor.js +115 -0
- package/dist/src/v2/receipt-core/predecessor.js.map +1 -0
- package/dist/src/v2/status-coverage/decide.d.ts +52 -0
- package/dist/src/v2/status-coverage/decide.d.ts.map +1 -0
- package/dist/src/v2/status-coverage/decide.js +398 -0
- package/dist/src/v2/status-coverage/decide.js.map +1 -0
- package/dist/src/v2/status-coverage/index.d.ts +3 -0
- package/dist/src/v2/status-coverage/index.d.ts.map +1 -0
- package/dist/src/v2/status-coverage/index.js +10 -0
- package/dist/src/v2/status-coverage/index.js.map +1 -0
- package/dist/src/v2/status-coverage/types.d.ts +258 -0
- package/dist/src/v2/status-coverage/types.d.ts.map +1 -0
- package/dist/src/v2/status-coverage/types.js +127 -0
- package/dist/src/v2/status-coverage/types.js.map +1 -0
- package/dist/src/v2/suspension/evaluate.d.ts +88 -0
- package/dist/src/v2/suspension/evaluate.d.ts.map +1 -0
- package/dist/src/v2/suspension/evaluate.js +404 -0
- package/dist/src/v2/suspension/evaluate.js.map +1 -0
- package/dist/src/v2/suspension/index.d.ts +3 -0
- package/dist/src/v2/suspension/index.d.ts.map +1 -0
- package/dist/src/v2/suspension/index.js +10 -0
- package/dist/src/v2/suspension/index.js.map +1 -0
- package/dist/src/v2/suspension/types.d.ts +243 -0
- package/dist/src/v2/suspension/types.d.ts.map +1 -0
- package/dist/src/v2/suspension/types.js +135 -0
- package/dist/src/v2/suspension/types.js.map +1 -0
- package/package.json +10 -7
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issue.d.ts","sourceRoot":"","sources":["../../../../src/v2/authority-revocation/issue.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AAY7E,OAAO,KAAK,EAA6B,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAElF,mFAAmF;AACnF,MAAM,WAAW,6BAA6B;IAC5C;;;;;yDAKqD;IACrD,GAAG,EAAE,MAAM,CAAA;IACX,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAA;IACf;;uFAEmF;IACnF,mBAAmB,EAAE,MAAM,CAAA;IAC3B,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAA;IACnB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,qBAAqB,EACjC,KAAK,EAAE,6BAA6B,EACpC,UAAU,EAAE,MAAM,GACjB,qBAAqB,CAmEvB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Copyright (c) 2026 Tymofii Pidlisnyi
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { authorityDelegationBody, computeAuthorityDelegationId, } from '../authority-delegation/canonical.js';
|
|
4
|
+
import { snapshotPlainData } from '../authority-delegation/plain-data.js';
|
|
5
|
+
import { isCanonicalTimestamp, validateAuthorityDelegationShape, } from '../authority-delegation/schema.js';
|
|
6
|
+
import { computeAuthorityRevocationCascadeTransactionIdForWrite, computeAuthorityRevocationIdForWrite, signAuthorityRevocation, } from './canonical.js';
|
|
7
|
+
import { validateAuthorityRevocationShape } from './schema.js';
|
|
8
|
+
import { AUTHORITY_REVOCATION_RECORD_TYPE, AUTHORITY_REVOCATION_VERSION, } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Mint a draft-03 section 3.5.1 direct revocation for one AuthorityDelegationV1.
|
|
11
|
+
*
|
|
12
|
+
* Section 3.5 line 634 states the only authorization rule this function applies: "Any
|
|
13
|
+
* delegation MAY be revoked by its issuer." The exact comparison is
|
|
14
|
+
* `input.revoker === delegation.issuer`, where `delegation` is the target record passed
|
|
15
|
+
* in by the caller and `issuer` is its own member. The record being minted is never
|
|
16
|
+
* consulted for its own authorization: a field a would-be revoker writes inside its own
|
|
17
|
+
* record cannot make that revoker the issuer of somebody else's delegation.
|
|
18
|
+
*
|
|
19
|
+
* The target's `delegation_id` is recomputed from its body before that comparison. A
|
|
20
|
+
* `delegation_id` sits outside the delegation's own identifier preimage, so a record
|
|
21
|
+
* whose claimed identifier does not match its body has an unauthenticated `issuer` too,
|
|
22
|
+
* and refusing there is what keeps the revoker check meaningful.
|
|
23
|
+
*
|
|
24
|
+
* What this function does NOT establish: that the holder of `privateKey` is in fact
|
|
25
|
+
* `input.revoker`, or that `input.verification_method` is one of that party's keys. An
|
|
26
|
+
* identifier-to-key binding is a resolver's answer, not a local one, and no string shape
|
|
27
|
+
* imposed on `verification_method` here could stand in for it. verifyAuthorityRevocation()
|
|
28
|
+
* makes that check, resolving the method under the TARGET delegation's `issuer` at
|
|
29
|
+
* `revoked_at`. Issuance can only refuse to mint a record that is already unverifiable; it
|
|
30
|
+
* cannot promise the record will verify.
|
|
31
|
+
*
|
|
32
|
+
* Nothing about a cascade over descendants is emitted here. This function produces one
|
|
33
|
+
* record about one delegation. Enforcement against descendants comes from chain
|
|
34
|
+
* verification, which rejects any chain containing a revoked ancestor.
|
|
35
|
+
*
|
|
36
|
+
* Throws on refusal; each message names its code in parentheses.
|
|
37
|
+
*/
|
|
38
|
+
export function issueAuthorityRevocation(delegation, input, privateKey) {
|
|
39
|
+
// Each member is read exactly once, so a getter or Proxy trap cannot answer one way
|
|
40
|
+
// when a value is checked and another way when it is hashed and signed.
|
|
41
|
+
const read = (key) => {
|
|
42
|
+
try {
|
|
43
|
+
return input ? input[key] : undefined;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const now = read('now');
|
|
50
|
+
const revoker = read('revoker');
|
|
51
|
+
const verificationMethod = read('verification_method');
|
|
52
|
+
const reasonCode = read('reason_code');
|
|
53
|
+
const hasDetail = (() => {
|
|
54
|
+
try {
|
|
55
|
+
return !!input && Object.prototype.hasOwnProperty.call(input, 'detail');
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
})();
|
|
61
|
+
const detail = hasDetail ? read('detail') : undefined;
|
|
62
|
+
const nonce = read('nonce');
|
|
63
|
+
if (!isCanonicalTimestamp(now)) {
|
|
64
|
+
throw new Error('authority revocation now must be a canonical UTC-millisecond timestamp (NONCANONICAL_VALUE)');
|
|
65
|
+
}
|
|
66
|
+
const target = snapshotPlainData(delegation);
|
|
67
|
+
const targetFailures = validateAuthorityDelegationShape(target);
|
|
68
|
+
if (targetFailures.length > 0) {
|
|
69
|
+
throw new Error(`authority revocation target delegation invalid (${targetFailures[0].code})`);
|
|
70
|
+
}
|
|
71
|
+
if (computeAuthorityDelegationId(authorityDelegationBody(target)) !== target.delegation_id) {
|
|
72
|
+
throw new Error('authority revocation target content address does not match its body (TARGET_ID_MISMATCH)');
|
|
73
|
+
}
|
|
74
|
+
// Draft section 3.5: a delegation may be revoked by its issuer. The compared fields are
|
|
75
|
+
// the caller's claimed revoker and the TARGET delegation's own `issuer` member.
|
|
76
|
+
if (typeof revoker !== 'string' || revoker !== target.issuer) {
|
|
77
|
+
throw new Error('authority revocation revoker is not the target delegation issuer (REVOKER_NOT_ISSUER)');
|
|
78
|
+
}
|
|
79
|
+
const origin = {
|
|
80
|
+
record_type: AUTHORITY_REVOCATION_RECORD_TYPE,
|
|
81
|
+
version: AUTHORITY_REVOCATION_VERSION,
|
|
82
|
+
delegation_id: target.delegation_id,
|
|
83
|
+
revoker,
|
|
84
|
+
verification_method: verificationMethod,
|
|
85
|
+
revoked_at: now,
|
|
86
|
+
reason_code: reasonCode,
|
|
87
|
+
...(hasDetail ? { detail: detail } : {}),
|
|
88
|
+
nonce: nonce,
|
|
89
|
+
};
|
|
90
|
+
const body = {
|
|
91
|
+
...origin,
|
|
92
|
+
cascade_transaction_id: computeAuthorityRevocationCascadeTransactionIdForWrite(origin),
|
|
93
|
+
};
|
|
94
|
+
// Shape is judged on the finished record, so the probe carries placeholder values in
|
|
95
|
+
// exactly the two members that are not derivable yet. Both are replaced below.
|
|
96
|
+
const probe = {
|
|
97
|
+
...body,
|
|
98
|
+
revocation_id: `sha256:${'0'.repeat(64)}`,
|
|
99
|
+
signature: '0'.repeat(128),
|
|
100
|
+
};
|
|
101
|
+
const failures = validateAuthorityRevocationShape(probe);
|
|
102
|
+
if (failures.length > 0) {
|
|
103
|
+
throw new Error(`authority revocation invalid: ${failures[0].message} (${failures[0].code})`);
|
|
104
|
+
}
|
|
105
|
+
const unsigned = {
|
|
106
|
+
...body,
|
|
107
|
+
revocation_id: computeAuthorityRevocationIdForWrite(body),
|
|
108
|
+
};
|
|
109
|
+
return { ...unsigned, signature: signAuthorityRevocation(unsigned, privateKey) };
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=issue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issue.js","sourceRoot":"","sources":["../../../../src/v2/authority-revocation/issue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { AuthorityDelegationV1 } from '../authority-delegation/types.js';
|
|
2
|
+
import type { AuthorityRevocationVerificationOptions } from './verify.js';
|
|
3
|
+
import type { AuthorityRevocationStore, AuthorityRevocationV1, AuthorityRevocationVerificationResult } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* What recordAuthorityRevocation() answers. Discriminated on `recorded`, so a caller
|
|
6
|
+
* cannot read `stored` without having decided which case it is in.
|
|
7
|
+
*
|
|
8
|
+
* - `recorded: true` — the candidate verified against its target. `stored` is what the
|
|
9
|
+
* store holds for that delegation now, and `inserted` says whether this call is the
|
|
10
|
+
* one that wrote it. `inserted: false` with `recorded: true` is the ordinary
|
|
11
|
+
* first-wins case: a valid revocation arrived second and the earlier record stands.
|
|
12
|
+
* - `recorded: false` — the candidate did not verify. `stored` is `undefined`, always,
|
|
13
|
+
* including when the store already holds a valid record for the same delegation. A
|
|
14
|
+
* rejected request is never handed somebody else's record as though it were its own
|
|
15
|
+
* result; `verification` carries the failures that rejected it.
|
|
16
|
+
*
|
|
17
|
+
* `verification` is the candidate's own result in both cases, never a result borrowed
|
|
18
|
+
* from a record already held.
|
|
19
|
+
*/
|
|
20
|
+
export type AuthorityRevocationRecordResult = {
|
|
21
|
+
recorded: true;
|
|
22
|
+
inserted: boolean;
|
|
23
|
+
stored: AuthorityRevocationV1;
|
|
24
|
+
verification: AuthorityRevocationVerificationResult;
|
|
25
|
+
} | {
|
|
26
|
+
recorded: false;
|
|
27
|
+
inserted: false;
|
|
28
|
+
stored: undefined;
|
|
29
|
+
verification: AuthorityRevocationVerificationResult;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Verify one candidate revocation against the delegation it names and, only then, offer
|
|
33
|
+
* it to `store` for the delegation's first-wins slot. The one supported way a revocation
|
|
34
|
+
* enters a store.
|
|
35
|
+
*
|
|
36
|
+
* The defect this closes: a store's write primitive takes a record and a record alone,
|
|
37
|
+
* and a store can verify nothing, because verification needs the target delegation and a
|
|
38
|
+
* key resolver and a store holds neither. A write path reachable with an arbitrary object
|
|
39
|
+
* therefore lets any object take the first-wins slot for a delegation, and because that
|
|
40
|
+
* slot is irreversible (INV-5), the delegation can never afterwards record the valid
|
|
41
|
+
* revocation that would have revoked it. The resolver's own re-verification keeps such a
|
|
42
|
+
* record from ever reading as 'revoked', so the damage is not a false revocation; it is a
|
|
43
|
+
* permanently blocked real one. Verification belongs before the write, which is here.
|
|
44
|
+
*
|
|
45
|
+
* Order of operations:
|
|
46
|
+
*
|
|
47
|
+
* 1. snapshot the candidate as plain JSON data, once
|
|
48
|
+
* 2. verify that snapshot against `delegation` under `options`
|
|
49
|
+
* 3. on anything but `valid`, return `recorded: false` with that verification result and
|
|
50
|
+
* no record, having touched the store not at all
|
|
51
|
+
* 4. on `valid`, hand the SNAPSHOT to store.insertVerifiedRevocation() and report what
|
|
52
|
+
* the store says it now holds
|
|
53
|
+
*
|
|
54
|
+
* Step 1 and step 4 use the same snapshot, so the bytes the store keeps are exactly the
|
|
55
|
+
* bytes that were verified. Passing the caller's original value instead would let a
|
|
56
|
+
* getter or a Proxy trap answer one way for the verifier and another for the store.
|
|
57
|
+
*
|
|
58
|
+
* `options` is the same key resolution the resolver takes; the key is resolved under the
|
|
59
|
+
* TARGET delegation's issuer, at the revocation's own `revoked_at`. See
|
|
60
|
+
* verifyAuthorityRevocation().
|
|
61
|
+
*
|
|
62
|
+
* Does not throw on a bad candidate: an unusable candidate, an unusable target and an
|
|
63
|
+
* unusable resolver are all verification results, and verifyAuthorityRevocation() does
|
|
64
|
+
* not throw. An exception raised by `store` itself is not caught. A store that cannot
|
|
65
|
+
* complete a write has not produced a verification outcome, and reporting its failure as
|
|
66
|
+
* one would be a lie about the record rather than about the store.
|
|
67
|
+
*/
|
|
68
|
+
export declare function recordAuthorityRevocation(store: AuthorityRevocationStore, delegation: AuthorityDelegationV1, candidate: unknown, options: AuthorityRevocationVerificationOptions): AuthorityRevocationRecordResult;
|
|
69
|
+
//# sourceMappingURL=record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../../../src/v2/authority-revocation/record.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AAE7E,OAAO,KAAK,EAAE,sCAAsC,EAAE,MAAM,aAAa,CAAA;AACzE,OAAO,KAAK,EACV,wBAAwB,EACxB,qBAAqB,EACrB,qCAAqC,EACtC,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,+BAA+B,GACvC;IACE,QAAQ,EAAE,IAAI,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,qBAAqB,CAAA;IAC7B,YAAY,EAAE,qCAAqC,CAAA;CACpD,GACD;IACE,QAAQ,EAAE,KAAK,CAAA;IACf,QAAQ,EAAE,KAAK,CAAA;IACf,MAAM,EAAE,SAAS,CAAA;IACjB,YAAY,EAAE,qCAAqC,CAAA;CACpD,CAAA;AAEL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,wBAAwB,EAC/B,UAAU,EAAE,qBAAqB,EACjC,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,sCAAsC,GAC9C,+BAA+B,CAQjC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Copyright (c) 2026 Tymofii Pidlisnyi
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { snapshotPlainData } from '../authority-delegation/plain-data.js';
|
|
4
|
+
import { verifyAuthorityRevocation } from './verify.js';
|
|
5
|
+
/**
|
|
6
|
+
* Verify one candidate revocation against the delegation it names and, only then, offer
|
|
7
|
+
* it to `store` for the delegation's first-wins slot. The one supported way a revocation
|
|
8
|
+
* enters a store.
|
|
9
|
+
*
|
|
10
|
+
* The defect this closes: a store's write primitive takes a record and a record alone,
|
|
11
|
+
* and a store can verify nothing, because verification needs the target delegation and a
|
|
12
|
+
* key resolver and a store holds neither. A write path reachable with an arbitrary object
|
|
13
|
+
* therefore lets any object take the first-wins slot for a delegation, and because that
|
|
14
|
+
* slot is irreversible (INV-5), the delegation can never afterwards record the valid
|
|
15
|
+
* revocation that would have revoked it. The resolver's own re-verification keeps such a
|
|
16
|
+
* record from ever reading as 'revoked', so the damage is not a false revocation; it is a
|
|
17
|
+
* permanently blocked real one. Verification belongs before the write, which is here.
|
|
18
|
+
*
|
|
19
|
+
* Order of operations:
|
|
20
|
+
*
|
|
21
|
+
* 1. snapshot the candidate as plain JSON data, once
|
|
22
|
+
* 2. verify that snapshot against `delegation` under `options`
|
|
23
|
+
* 3. on anything but `valid`, return `recorded: false` with that verification result and
|
|
24
|
+
* no record, having touched the store not at all
|
|
25
|
+
* 4. on `valid`, hand the SNAPSHOT to store.insertVerifiedRevocation() and report what
|
|
26
|
+
* the store says it now holds
|
|
27
|
+
*
|
|
28
|
+
* Step 1 and step 4 use the same snapshot, so the bytes the store keeps are exactly the
|
|
29
|
+
* bytes that were verified. Passing the caller's original value instead would let a
|
|
30
|
+
* getter or a Proxy trap answer one way for the verifier and another for the store.
|
|
31
|
+
*
|
|
32
|
+
* `options` is the same key resolution the resolver takes; the key is resolved under the
|
|
33
|
+
* TARGET delegation's issuer, at the revocation's own `revoked_at`. See
|
|
34
|
+
* verifyAuthorityRevocation().
|
|
35
|
+
*
|
|
36
|
+
* Does not throw on a bad candidate: an unusable candidate, an unusable target and an
|
|
37
|
+
* unusable resolver are all verification results, and verifyAuthorityRevocation() does
|
|
38
|
+
* not throw. An exception raised by `store` itself is not caught. A store that cannot
|
|
39
|
+
* complete a write has not produced a verification outcome, and reporting its failure as
|
|
40
|
+
* one would be a lie about the record rather than about the store.
|
|
41
|
+
*/
|
|
42
|
+
export function recordAuthorityRevocation(store, delegation, candidate, options) {
|
|
43
|
+
const snapshot = snapshotPlainData(candidate);
|
|
44
|
+
const verification = verifyAuthorityRevocation(snapshot, delegation, options);
|
|
45
|
+
if (!verification.valid) {
|
|
46
|
+
return { recorded: false, inserted: false, stored: undefined, verification };
|
|
47
|
+
}
|
|
48
|
+
const { inserted, stored } = store.insertVerifiedRevocation(snapshot);
|
|
49
|
+
return { recorded: true, inserted, stored, verification };
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../../../src/v2/authority-revocation/record.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,sCAAsC;AAEtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAEzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAsCvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAA+B,EAC/B,UAAiC,EACjC,SAAkB,EAClB,OAA+C;IAE/C,MAAM,QAAQ,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAA;IAC7C,MAAM,YAAY,GAAG,yBAAyB,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IAC7E,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QACxB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,CAAA;IAC9E,CAAC;IACD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,wBAAwB,CAAC,QAAiC,CAAC,CAAA;IAC9F,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,CAAA;AAC3D,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AuthorityDelegationV1, RevocationResolution } from '../authority-delegation/types.js';
|
|
2
|
+
import type { AuthorityRevocationVerificationOptions } from './verify.js';
|
|
3
|
+
import type { AuthorityRevocationStore } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Adapt an AuthorityRevocationStore to the resolver verifyAuthorityDelegationChain and
|
|
6
|
+
* issueSubAuthorityDelegation already take.
|
|
7
|
+
*
|
|
8
|
+
* The three answers, and what each one costs to earn:
|
|
9
|
+
*
|
|
10
|
+
* - 'revoked' only when the store holds a record for this delegation AND that record
|
|
11
|
+
* verifies against this delegation under verifyAuthorityRevocation(). A stored record
|
|
12
|
+
* that does not verify yields 'unknown', not 'revoked' and not 'active': the store has
|
|
13
|
+
* an opinion this resolver cannot confirm, which is exactly indeterminate.
|
|
14
|
+
* - 'active' only when the store says it tracks this delegation and holds no revocation
|
|
15
|
+
* for it. Absence from a store is never 'active' on its own. A store that has never
|
|
16
|
+
* heard of a delegation has not said the delegation is unrevoked.
|
|
17
|
+
* - 'unknown' for everything else, including a store that throws and a delegation whose
|
|
18
|
+
* `delegation_id` does not recompute from its own body.
|
|
19
|
+
*
|
|
20
|
+
* The chain verifier treats 'unknown' as indeterminate (REVOCATION_UNKNOWN) and
|
|
21
|
+
* issueSubAuthorityDelegation refuses to mint under anything but 'active', so the
|
|
22
|
+
* fail-closed direction is already theirs; this resolver only has to avoid manufacturing
|
|
23
|
+
* an 'active' it cannot support.
|
|
24
|
+
*
|
|
25
|
+
* Never throws. A resolver that threw would be caught by both callers and read as
|
|
26
|
+
* 'unknown' anyway, so it returns 'unknown' in the open rather than through a catch in
|
|
27
|
+
* somebody else's code.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createAuthorityRevocationResolver(store: AuthorityRevocationStore, options: AuthorityRevocationVerificationOptions): (delegation: AuthorityDelegationV1) => RevocationResolution;
|
|
30
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../../src/v2/authority-revocation/resolver.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,kCAAkC,CAAA;AAEzC,OAAO,KAAK,EAAE,sCAAsC,EAAE,MAAM,aAAa,CAAA;AACzE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,wBAAwB,EAC/B,OAAO,EAAE,sCAAsC,GAC9C,CAAC,UAAU,EAAE,qBAAqB,KAAK,oBAAoB,CAoB7D"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Copyright (c) 2026 Tymofii Pidlisnyi
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { authorityDelegationBody, computeAuthorityDelegationId, } from '../authority-delegation/canonical.js';
|
|
4
|
+
import { verifyAuthorityRevocation } from './verify.js';
|
|
5
|
+
/**
|
|
6
|
+
* Adapt an AuthorityRevocationStore to the resolver verifyAuthorityDelegationChain and
|
|
7
|
+
* issueSubAuthorityDelegation already take.
|
|
8
|
+
*
|
|
9
|
+
* The three answers, and what each one costs to earn:
|
|
10
|
+
*
|
|
11
|
+
* - 'revoked' only when the store holds a record for this delegation AND that record
|
|
12
|
+
* verifies against this delegation under verifyAuthorityRevocation(). A stored record
|
|
13
|
+
* that does not verify yields 'unknown', not 'revoked' and not 'active': the store has
|
|
14
|
+
* an opinion this resolver cannot confirm, which is exactly indeterminate.
|
|
15
|
+
* - 'active' only when the store says it tracks this delegation and holds no revocation
|
|
16
|
+
* for it. Absence from a store is never 'active' on its own. A store that has never
|
|
17
|
+
* heard of a delegation has not said the delegation is unrevoked.
|
|
18
|
+
* - 'unknown' for everything else, including a store that throws and a delegation whose
|
|
19
|
+
* `delegation_id` does not recompute from its own body.
|
|
20
|
+
*
|
|
21
|
+
* The chain verifier treats 'unknown' as indeterminate (REVOCATION_UNKNOWN) and
|
|
22
|
+
* issueSubAuthorityDelegation refuses to mint under anything but 'active', so the
|
|
23
|
+
* fail-closed direction is already theirs; this resolver only has to avoid manufacturing
|
|
24
|
+
* an 'active' it cannot support.
|
|
25
|
+
*
|
|
26
|
+
* Never throws. A resolver that threw would be caught by both callers and read as
|
|
27
|
+
* 'unknown' anyway, so it returns 'unknown' in the open rather than through a catch in
|
|
28
|
+
* somebody else's code.
|
|
29
|
+
*/
|
|
30
|
+
export function createAuthorityRevocationResolver(store, options) {
|
|
31
|
+
return (delegation) => {
|
|
32
|
+
try {
|
|
33
|
+
// A claimed delegation_id sits outside the delegation's own identifier preimage.
|
|
34
|
+
// Looking a revocation up by an unauthenticated label would let a caller ask about
|
|
35
|
+
// one delegation while presenting another.
|
|
36
|
+
const delegationId = computeAuthorityDelegationId(authorityDelegationBody(delegation));
|
|
37
|
+
if (delegationId !== delegation.delegation_id)
|
|
38
|
+
return 'unknown';
|
|
39
|
+
const record = store.get(delegationId);
|
|
40
|
+
if (record !== undefined) {
|
|
41
|
+
return verifyAuthorityRevocation(record, delegation, options).state === 'valid'
|
|
42
|
+
? 'revoked'
|
|
43
|
+
: 'unknown';
|
|
44
|
+
}
|
|
45
|
+
return store.tracks(delegationId) ? 'active' : 'unknown';
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return 'unknown';
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../../../src/v2/authority-revocation/resolver.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,sCAAsC;AAEtC,OAAO,EACL,uBAAuB,EACvB,4BAA4B,GAC7B,MAAM,sCAAsC,CAAA;AAK7C,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAIvD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,iCAAiC,CAC/C,KAA+B,EAC/B,OAA+C;IAE/C,OAAO,CAAC,UAAiC,EAAwB,EAAE;QACjE,IAAI,CAAC;YACH,iFAAiF;YACjF,mFAAmF;YACnF,2CAA2C;YAC3C,MAAM,YAAY,GAAG,4BAA4B,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC,CAAA;YACtF,IAAI,YAAY,KAAK,UAAU,CAAC,aAAa;gBAAE,OAAO,SAAS,CAAA;YAE/D,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;YACtC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,OAAO,yBAAyB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,KAAK,KAAK,OAAO;oBAC7E,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC,SAAS,CAAA;YACf,CAAC;YACD,OAAO,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAA;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAA;QAClB,CAAC;IACH,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AuthorityRevocationFailure, AuthorityRevocationV1 } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Closed-schema and canonical-value validation for an in-memory decoded revocation.
|
|
4
|
+
*
|
|
5
|
+
* Snapshots `value` to plain JSON data first (see authority-delegation/plain-data.ts) and
|
|
6
|
+
* judges only that snapshot, so a getter or Proxy trap in the caller's argument is read
|
|
7
|
+
* at most once and cannot answer this function's checks differently from the value a
|
|
8
|
+
* caller goes on to hash.
|
|
9
|
+
*
|
|
10
|
+
* Returns every failure it finds rather than the first, matching
|
|
11
|
+
* validateAuthorityDelegationShape(). The caller decides which one it reports.
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateAuthorityRevocationShape(value: unknown): AuthorityRevocationFailure[];
|
|
14
|
+
export declare function isAuthorityRevocationV1(value: unknown): value is AuthorityRevocationV1;
|
|
15
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/v2/authority-revocation/schema.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,0BAA0B,EAE1B,qBAAqB,EACtB,MAAM,YAAY,CAAA;AAqCnB;;;;;;;;;;GAUG;AACH,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,0BAA0B,EAAE,CA2D7F;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAEtF"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Copyright (c) 2026 Tymofii Pidlisnyi
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { snapshotPlainData } from '../authority-delegation/plain-data.js';
|
|
4
|
+
import { isCanonicalTimestamp } from '../authority-delegation/schema.js';
|
|
5
|
+
import { AUTHORITY_REVOCATION_RECORD_TYPE, AUTHORITY_REVOCATION_VERSION, } from './types.js';
|
|
6
|
+
const CONTENT_ADDRESS = /^sha256:[0-9a-f]{64}$/;
|
|
7
|
+
const HEX_32 = /^[0-9a-f]{32}$/;
|
|
8
|
+
const HEX_128 = /^[0-9a-f]{128}$/;
|
|
9
|
+
/** Every member a valid record may carry. A member outside this list is SCHEMA_INVALID:
|
|
10
|
+
* the schema is closed, so an unknown field cannot ride inside a signed preimage
|
|
11
|
+
* unnoticed. `detail` is the only OPTIONAL one. */
|
|
12
|
+
const REQUIRED_KEYS = [
|
|
13
|
+
'record_type',
|
|
14
|
+
'version',
|
|
15
|
+
'revocation_id',
|
|
16
|
+
'delegation_id',
|
|
17
|
+
'revoker',
|
|
18
|
+
'verification_method',
|
|
19
|
+
'revoked_at',
|
|
20
|
+
'reason_code',
|
|
21
|
+
'cascade_transaction_id',
|
|
22
|
+
'nonce',
|
|
23
|
+
'signature',
|
|
24
|
+
];
|
|
25
|
+
const OPTIONAL_KEYS = ['detail'];
|
|
26
|
+
function failure(code, message) {
|
|
27
|
+
return { code, message };
|
|
28
|
+
}
|
|
29
|
+
function record(value) {
|
|
30
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
31
|
+
? value
|
|
32
|
+
: null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Closed-schema and canonical-value validation for an in-memory decoded revocation.
|
|
36
|
+
*
|
|
37
|
+
* Snapshots `value` to plain JSON data first (see authority-delegation/plain-data.ts) and
|
|
38
|
+
* judges only that snapshot, so a getter or Proxy trap in the caller's argument is read
|
|
39
|
+
* at most once and cannot answer this function's checks differently from the value a
|
|
40
|
+
* caller goes on to hash.
|
|
41
|
+
*
|
|
42
|
+
* Returns every failure it finds rather than the first, matching
|
|
43
|
+
* validateAuthorityDelegationShape(). The caller decides which one it reports.
|
|
44
|
+
*/
|
|
45
|
+
export function validateAuthorityRevocationShape(value) {
|
|
46
|
+
const failures = [];
|
|
47
|
+
const top = record(snapshotPlainData(value));
|
|
48
|
+
if (!top)
|
|
49
|
+
return [failure('SCHEMA_INVALID', 'revocation must be a JSON object')];
|
|
50
|
+
if (top.record_type !== AUTHORITY_REVOCATION_RECORD_TYPE) {
|
|
51
|
+
return [failure('UNSUPPORTED_RECORD_TYPE', `record_type must be ${AUTHORITY_REVOCATION_RECORD_TYPE}`)];
|
|
52
|
+
}
|
|
53
|
+
if (top.version !== AUTHORITY_REVOCATION_VERSION) {
|
|
54
|
+
return [failure('UNSUPPORTED_VERSION', `version must be ${AUTHORITY_REVOCATION_VERSION}`)];
|
|
55
|
+
}
|
|
56
|
+
for (const key of REQUIRED_KEYS) {
|
|
57
|
+
if (!Object.prototype.hasOwnProperty.call(top, key)) {
|
|
58
|
+
failures.push(failure('SCHEMA_INVALID', `${key} is required`));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const allowed = new Set([...REQUIRED_KEYS, ...OPTIONAL_KEYS]);
|
|
62
|
+
for (const key of Object.keys(top)) {
|
|
63
|
+
if (!allowed.has(key))
|
|
64
|
+
failures.push(failure('SCHEMA_INVALID', `unknown member ${key}`));
|
|
65
|
+
}
|
|
66
|
+
if (typeof top.revocation_id !== 'string' || !CONTENT_ADDRESS.test(top.revocation_id)) {
|
|
67
|
+
failures.push(failure('SCHEMA_INVALID', 'revocation_id must be sha256:<64 lowercase hex>'));
|
|
68
|
+
}
|
|
69
|
+
if (typeof top.delegation_id !== 'string' || !CONTENT_ADDRESS.test(top.delegation_id)) {
|
|
70
|
+
failures.push(failure('SCHEMA_INVALID', 'delegation_id must be sha256:<64 lowercase hex>'));
|
|
71
|
+
}
|
|
72
|
+
if (typeof top.cascade_transaction_id !== 'string' || !CONTENT_ADDRESS.test(top.cascade_transaction_id)) {
|
|
73
|
+
failures.push(failure('SCHEMA_INVALID', 'cascade_transaction_id must be sha256:<64 lowercase hex>'));
|
|
74
|
+
}
|
|
75
|
+
if (typeof top.revoker !== 'string' || top.revoker.length === 0) {
|
|
76
|
+
failures.push(failure('SCHEMA_INVALID', 'revoker must be a non-empty string'));
|
|
77
|
+
}
|
|
78
|
+
// Nothing here judges whether the method belongs to the revoker. That is decided only by
|
|
79
|
+
// key resolution against the target delegation's `issuer` at `revoked_at`, which
|
|
80
|
+
// verifyAuthorityRevocation() performs; a local string shape cannot establish it, and a
|
|
81
|
+
// method identifier need not be a fragment of the identifier that controls it.
|
|
82
|
+
if (typeof top.verification_method !== 'string' || top.verification_method.length === 0) {
|
|
83
|
+
failures.push(failure('SCHEMA_INVALID', 'verification_method must be a non-empty string'));
|
|
84
|
+
}
|
|
85
|
+
if (!isCanonicalTimestamp(top.revoked_at)) {
|
|
86
|
+
failures.push(failure('NONCANONICAL_VALUE', 'revoked_at must be a canonical UTC-millisecond timestamp'));
|
|
87
|
+
}
|
|
88
|
+
// The draft names a machine-readable reason code and fixes no grammar for one, so no
|
|
89
|
+
// grammar is invented here.
|
|
90
|
+
if (typeof top.reason_code !== 'string' || top.reason_code.length === 0) {
|
|
91
|
+
failures.push(failure('SCHEMA_INVALID', 'reason_code must be a non-empty string'));
|
|
92
|
+
}
|
|
93
|
+
if (Object.prototype.hasOwnProperty.call(top, 'detail') && typeof top.detail !== 'string') {
|
|
94
|
+
failures.push(failure('SCHEMA_INVALID', 'detail, when present, must be a string'));
|
|
95
|
+
}
|
|
96
|
+
if (typeof top.nonce !== 'string' || !HEX_32.test(top.nonce)) {
|
|
97
|
+
failures.push(failure('NONCANONICAL_VALUE', 'nonce must be 32 lowercase hex characters'));
|
|
98
|
+
}
|
|
99
|
+
if (typeof top.signature !== 'string' || !HEX_128.test(top.signature)) {
|
|
100
|
+
failures.push(failure('SCHEMA_INVALID', 'signature must be 128 lowercase hex characters'));
|
|
101
|
+
}
|
|
102
|
+
return failures;
|
|
103
|
+
}
|
|
104
|
+
export function isAuthorityRevocationV1(value) {
|
|
105
|
+
return validateAuthorityRevocationShape(value).length === 0;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../../src/v2/authority-revocation/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { AuthorityRevocationInsertion, AuthorityRevocationStore, AuthorityRevocationV1 } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reference AuthorityRevocationStore holding every record in this process's heap.
|
|
4
|
+
*
|
|
5
|
+
* For tests, fixtures and local reference use. It is not a persistence layer and does not
|
|
6
|
+
* become one: the records live in two Maps and are gone when the process exits, when the
|
|
7
|
+
* object is garbage collected, or when a second process asks the same question. Draft
|
|
8
|
+
* section 3.5.1 makes cascade completion depend on a descendant's revocation being
|
|
9
|
+
* PERSISTENT, and nothing here can establish that, which is why this module emits no
|
|
10
|
+
* completion record.
|
|
11
|
+
*
|
|
12
|
+
* Behavior this class fixes, none of it settled by draft text:
|
|
13
|
+
*
|
|
14
|
+
* - First verified revocation for a delegation wins. INV-5 makes revocation
|
|
15
|
+
* irreversible, and a store that let a second record displace the first would make the
|
|
16
|
+
* recorded revocation time, reason and revoker mutable after the fact.
|
|
17
|
+
* - insertVerifiedRevocation() for a delegation that already has a record reports
|
|
18
|
+
* `inserted: false` and returns the record already held, and the caller's record is
|
|
19
|
+
* discarded. A repeated request is therefore idempotent in what the store reports, not
|
|
20
|
+
* in the bytes the caller minted: two calls with different nonces mint two different
|
|
21
|
+
* valid records, and the store keeps the first.
|
|
22
|
+
* - insertVerifiedRevocation() brings its target into the tracked view. Recording a
|
|
23
|
+
* revocation for a delegation is a statement that this store has an opinion about that
|
|
24
|
+
* delegation.
|
|
25
|
+
*
|
|
26
|
+
* insertVerifiedRevocation() is a persistence primitive and not an entry point. It does
|
|
27
|
+
* not verify, and a store cannot: verification needs the target delegation and a key
|
|
28
|
+
* resolver, neither of which is here. recordAuthorityRevocation() in record.ts is the
|
|
29
|
+
* one supported way in; it verifies against the target and calls this method only on a
|
|
30
|
+
* `valid` result. Nothing about that split relaxes the resolver, which still verifies on
|
|
31
|
+
* the way out, so a record that reached this store by some other route still cannot
|
|
32
|
+
* produce a 'revoked' answer.
|
|
33
|
+
*/
|
|
34
|
+
export declare class InMemoryAuthorityRevocationStore implements AuthorityRevocationStore {
|
|
35
|
+
private readonly tracked;
|
|
36
|
+
private readonly records;
|
|
37
|
+
/** Bring `delegationId` into this store's view with no revocation recorded for it.
|
|
38
|
+
*
|
|
39
|
+
* Not part of AuthorityRevocationStore: how a store learns which delegations it covers
|
|
40
|
+
* is an implementation's own business, and a database-backed store would answer
|
|
41
|
+
* tracks() from its own rows rather than from a method like this one. */
|
|
42
|
+
track(delegationId: string): void;
|
|
43
|
+
tracks(delegationId: string): boolean;
|
|
44
|
+
get(delegationId: string): AuthorityRevocationV1 | undefined;
|
|
45
|
+
/** See AuthorityRevocationStore.insertVerifiedRevocation for the contract, including
|
|
46
|
+
* that `revocation` must already have been verified against its target delegation by
|
|
47
|
+
* recordAuthorityRevocation().
|
|
48
|
+
*
|
|
49
|
+
* The read of the existing record and the write are one synchronous statement sequence
|
|
50
|
+
* with no await and no callback between them, so on this single-threaded runtime no
|
|
51
|
+
* other caller can interleave and no second caller can observe `inserted: true` for a
|
|
52
|
+
* delegation whose slot is already taken. */
|
|
53
|
+
insertVerifiedRevocation(revocation: AuthorityRevocationV1): AuthorityRevocationInsertion;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../../src/v2/authority-revocation/store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,4BAA4B,EAC5B,wBAAwB,EACxB,qBAAqB,EACtB,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAAa,gCAAiC,YAAW,wBAAwB;IAC/E,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;IAEnE;;;;8EAI0E;IAC1E,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAEhC;IAED,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAEpC;IAED,GAAG,CAAC,YAAY,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAE3D;IAED;;;;;;;kDAO8C;IAC9C,wBAAwB,CAAC,UAAU,EAAE,qBAAqB,GAAG,4BAA4B,CAMxF;CACF"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Copyright (c) 2026 Tymofii Pidlisnyi
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Reference AuthorityRevocationStore holding every record in this process's heap.
|
|
5
|
+
*
|
|
6
|
+
* For tests, fixtures and local reference use. It is not a persistence layer and does not
|
|
7
|
+
* become one: the records live in two Maps and are gone when the process exits, when the
|
|
8
|
+
* object is garbage collected, or when a second process asks the same question. Draft
|
|
9
|
+
* section 3.5.1 makes cascade completion depend on a descendant's revocation being
|
|
10
|
+
* PERSISTENT, and nothing here can establish that, which is why this module emits no
|
|
11
|
+
* completion record.
|
|
12
|
+
*
|
|
13
|
+
* Behavior this class fixes, none of it settled by draft text:
|
|
14
|
+
*
|
|
15
|
+
* - First verified revocation for a delegation wins. INV-5 makes revocation
|
|
16
|
+
* irreversible, and a store that let a second record displace the first would make the
|
|
17
|
+
* recorded revocation time, reason and revoker mutable after the fact.
|
|
18
|
+
* - insertVerifiedRevocation() for a delegation that already has a record reports
|
|
19
|
+
* `inserted: false` and returns the record already held, and the caller's record is
|
|
20
|
+
* discarded. A repeated request is therefore idempotent in what the store reports, not
|
|
21
|
+
* in the bytes the caller minted: two calls with different nonces mint two different
|
|
22
|
+
* valid records, and the store keeps the first.
|
|
23
|
+
* - insertVerifiedRevocation() brings its target into the tracked view. Recording a
|
|
24
|
+
* revocation for a delegation is a statement that this store has an opinion about that
|
|
25
|
+
* delegation.
|
|
26
|
+
*
|
|
27
|
+
* insertVerifiedRevocation() is a persistence primitive and not an entry point. It does
|
|
28
|
+
* not verify, and a store cannot: verification needs the target delegation and a key
|
|
29
|
+
* resolver, neither of which is here. recordAuthorityRevocation() in record.ts is the
|
|
30
|
+
* one supported way in; it verifies against the target and calls this method only on a
|
|
31
|
+
* `valid` result. Nothing about that split relaxes the resolver, which still verifies on
|
|
32
|
+
* the way out, so a record that reached this store by some other route still cannot
|
|
33
|
+
* produce a 'revoked' answer.
|
|
34
|
+
*/
|
|
35
|
+
export class InMemoryAuthorityRevocationStore {
|
|
36
|
+
tracked = new Set();
|
|
37
|
+
records = new Map();
|
|
38
|
+
/** Bring `delegationId` into this store's view with no revocation recorded for it.
|
|
39
|
+
*
|
|
40
|
+
* Not part of AuthorityRevocationStore: how a store learns which delegations it covers
|
|
41
|
+
* is an implementation's own business, and a database-backed store would answer
|
|
42
|
+
* tracks() from its own rows rather than from a method like this one. */
|
|
43
|
+
track(delegationId) {
|
|
44
|
+
this.tracked.add(delegationId);
|
|
45
|
+
}
|
|
46
|
+
tracks(delegationId) {
|
|
47
|
+
return this.tracked.has(delegationId);
|
|
48
|
+
}
|
|
49
|
+
get(delegationId) {
|
|
50
|
+
return this.records.get(delegationId);
|
|
51
|
+
}
|
|
52
|
+
/** See AuthorityRevocationStore.insertVerifiedRevocation for the contract, including
|
|
53
|
+
* that `revocation` must already have been verified against its target delegation by
|
|
54
|
+
* recordAuthorityRevocation().
|
|
55
|
+
*
|
|
56
|
+
* The read of the existing record and the write are one synchronous statement sequence
|
|
57
|
+
* with no await and no callback between them, so on this single-threaded runtime no
|
|
58
|
+
* other caller can interleave and no second caller can observe `inserted: true` for a
|
|
59
|
+
* delegation whose slot is already taken. */
|
|
60
|
+
insertVerifiedRevocation(revocation) {
|
|
61
|
+
const existing = this.records.get(revocation.delegation_id);
|
|
62
|
+
if (existing)
|
|
63
|
+
return { inserted: false, stored: existing };
|
|
64
|
+
this.records.set(revocation.delegation_id, revocation);
|
|
65
|
+
this.tracked.add(revocation.delegation_id);
|
|
66
|
+
return { inserted: true, stored: revocation };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../src/v2/authority-revocation/store.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,sCAAsC;AAQtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,OAAO,gCAAgC;IAC1B,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IAC3B,OAAO,GAAG,IAAI,GAAG,EAAiC,CAAA;IAEnE;;;;8EAI0E;IAC1E,KAAK,CAAC,YAAoB;QACxB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;IAChC,CAAC;IAED,MAAM,CAAC,YAAoB;QACzB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;IACvC,CAAC;IAED,GAAG,CAAC,YAAoB;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;IACvC,CAAC;IAED;;;;;;;kDAO8C;IAC9C,wBAAwB,CAAC,UAAiC;QACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,CAAA;QAC3D,IAAI,QAAQ;YAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAA;QAC1D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,EAAE,UAAU,CAAC,CAAA;QACtD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,CAAA;QAC1C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAA;IAC/C,CAAC;CACF"}
|