@orangecheck/agent-core 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +14 -3
- package/dist/index.d.ts +14 -3
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +63 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/assert-scope.test.ts +72 -0
- package/src/assert-scope.ts +80 -0
- package/src/federation.test.ts +1 -0
- package/src/index.ts +3 -0
- package/src/private-scope.test.ts +4 -3
- package/src/revocation-failclosed.test.ts +67 -0
- package/src/test-vectors.test.ts +5 -0
- package/src/verify.ts +56 -6
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentErrorCode, ActionEnvelope, DelegationEnvelope, SubdelegationEnvelope,
|
|
1
|
+
import { AgentErrorCode, RevocationEnvelope, ActionEnvelope, DelegationEnvelope, SubdelegationEnvelope, ChainLink, VerifyActionResult, VerifyDelegationResult, VerifyRevocationResult, VerifySubdelegationResult, ActorRef, DelegationBond, DelegationRevocationRef, ScopesEncryptedEnvelope } from './types.mjs';
|
|
2
2
|
export { ActionCanonicalInput, ActionContent, ActionOts, DelegationCanonicalInput, ENVELOPE_VERSION, EnvelopeKind, RevocationCanonicalInput, RevocationHolder, Signature, SubdelegationCanonicalInput, VerifyActionOkExtra, VerifyErr, VerifyOk } from './types.mjs';
|
|
3
3
|
export { actionCanonicalBytes, actionCanonicalMessage, canonicalActionBytes, canonicalDelegationBytes, canonicalRevocationBytes, canonicalizeAction, canonicalizeDelegation, canonicalizeRevocation, canonicalizeScopes, computeActionId, computeDelegationId, computeRevocationId, computeSubdelegationId, delegationCanonicalBytes, delegationCanonicalMessage, parseAndCanonicalizeScopes, revocationCanonicalBytes, revocationCanonicalMessage, sha256Hex, subdelegationCanonicalBytes, subdelegationCanonicalMessage } from './canonical.mjs';
|
|
4
4
|
import { ValidationOptions } from './scope.mjs';
|
|
@@ -11,6 +11,8 @@ interface VerifyBase {
|
|
|
11
11
|
verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;
|
|
12
12
|
skipSignatureVerification?: boolean;
|
|
13
13
|
scopeMode?: ValidationOptions['mode'];
|
|
14
|
+
skipRevocationCheck?: boolean;
|
|
15
|
+
revocations?: RevocationEnvelope[];
|
|
14
16
|
}
|
|
15
17
|
declare class AgentError extends Error {
|
|
16
18
|
code: AgentErrorCode;
|
|
@@ -31,7 +33,6 @@ interface VerifyActionInput extends VerifyBase {
|
|
|
31
33
|
delegation: DelegationEnvelope;
|
|
32
34
|
subdelegationChain?: SubdelegationEnvelope[];
|
|
33
35
|
maxChainDepth?: number;
|
|
34
|
-
revocations?: RevocationEnvelope[];
|
|
35
36
|
content?: Uint8Array;
|
|
36
37
|
verifyOtsAnchor?: (proofB64: string, blockHeight: number, blockHash: string) => Promise<boolean>;
|
|
37
38
|
resolveAnchorBlockHeight?: (env: ActionEnvelope | RevocationEnvelope) => number | null;
|
|
@@ -165,4 +166,14 @@ declare function hasPrivateScopes<T extends {
|
|
|
165
166
|
scopes_encrypted: ScopesEncryptedEnvelope;
|
|
166
167
|
};
|
|
167
168
|
|
|
168
|
-
|
|
169
|
+
interface ScopeBearingDelegation {
|
|
170
|
+
scopes?: string[];
|
|
171
|
+
scopes_encrypted?: ScopesEncryptedEnvelope;
|
|
172
|
+
}
|
|
173
|
+
declare class ScopeNotGrantedError extends Error {
|
|
174
|
+
readonly reason: 'not_subscope' | 'scopes_encrypted' | 'no_scopes';
|
|
175
|
+
constructor(reason: ScopeNotGrantedError['reason'], message: string);
|
|
176
|
+
}
|
|
177
|
+
declare function assertScopeGranted(delegation: ScopeBearingDelegation, scopeExercised: string, fnName: string): void;
|
|
178
|
+
|
|
179
|
+
export { ActionEnvelope, ActorRef, AgentError, AgentErrorCode, ChainLink, DEFAULT_MAX_CHAIN_DEPTH, DelegationBond, DelegationEnvelope, DelegationRevocationRef, type FederationDelegationEnvelope, type FederationDescriptor, type FederationGuardian, type FederationPrincipal, type FederationRevocationEnvelope, type FederationSignature, type FederationVerifyResult, RevocationEnvelope, type ScopeBearingDelegation, ScopeNotGrantedError, ScopesEncryptedEnvelope, type SealScopesInput, SubdelegationEnvelope, type UnsealScopesInput, type UnsealedScopes, ValidationOptions, type VerifyActionInput, VerifyActionResult, type VerifyBase, type VerifyDelegationInput, VerifyDelegationResult, type VerifyFederationBase, type VerifyFederationDelegationInput, type VerifyFederationRevocationInput, type VerifyRevocationInput, VerifyRevocationResult, type VerifySubdelegationInput, VerifySubdelegationResult, assertScopeGranted, computeFederationDescriptorId, decodeScopesPayload, encodeScopesPayload, federationDescriptorCanonicalMessage, hasPrivateScopes, sealScopes, unsealScopes, verifyAction, verifyDelegation, verifyFederationDelegation, verifyFederationRevocation, verifyRevocation, verifySubdelegation };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentErrorCode, ActionEnvelope, DelegationEnvelope, SubdelegationEnvelope,
|
|
1
|
+
import { AgentErrorCode, RevocationEnvelope, ActionEnvelope, DelegationEnvelope, SubdelegationEnvelope, ChainLink, VerifyActionResult, VerifyDelegationResult, VerifyRevocationResult, VerifySubdelegationResult, ActorRef, DelegationBond, DelegationRevocationRef, ScopesEncryptedEnvelope } from './types.js';
|
|
2
2
|
export { ActionCanonicalInput, ActionContent, ActionOts, DelegationCanonicalInput, ENVELOPE_VERSION, EnvelopeKind, RevocationCanonicalInput, RevocationHolder, Signature, SubdelegationCanonicalInput, VerifyActionOkExtra, VerifyErr, VerifyOk } from './types.js';
|
|
3
3
|
export { actionCanonicalBytes, actionCanonicalMessage, canonicalActionBytes, canonicalDelegationBytes, canonicalRevocationBytes, canonicalizeAction, canonicalizeDelegation, canonicalizeRevocation, canonicalizeScopes, computeActionId, computeDelegationId, computeRevocationId, computeSubdelegationId, delegationCanonicalBytes, delegationCanonicalMessage, parseAndCanonicalizeScopes, revocationCanonicalBytes, revocationCanonicalMessage, sha256Hex, subdelegationCanonicalBytes, subdelegationCanonicalMessage } from './canonical.js';
|
|
4
4
|
import { ValidationOptions } from './scope.js';
|
|
@@ -11,6 +11,8 @@ interface VerifyBase {
|
|
|
11
11
|
verifyBip322?: (msg: string, signatureB64: string, address: string) => Promise<boolean>;
|
|
12
12
|
skipSignatureVerification?: boolean;
|
|
13
13
|
scopeMode?: ValidationOptions['mode'];
|
|
14
|
+
skipRevocationCheck?: boolean;
|
|
15
|
+
revocations?: RevocationEnvelope[];
|
|
14
16
|
}
|
|
15
17
|
declare class AgentError extends Error {
|
|
16
18
|
code: AgentErrorCode;
|
|
@@ -31,7 +33,6 @@ interface VerifyActionInput extends VerifyBase {
|
|
|
31
33
|
delegation: DelegationEnvelope;
|
|
32
34
|
subdelegationChain?: SubdelegationEnvelope[];
|
|
33
35
|
maxChainDepth?: number;
|
|
34
|
-
revocations?: RevocationEnvelope[];
|
|
35
36
|
content?: Uint8Array;
|
|
36
37
|
verifyOtsAnchor?: (proofB64: string, blockHeight: number, blockHash: string) => Promise<boolean>;
|
|
37
38
|
resolveAnchorBlockHeight?: (env: ActionEnvelope | RevocationEnvelope) => number | null;
|
|
@@ -165,4 +166,14 @@ declare function hasPrivateScopes<T extends {
|
|
|
165
166
|
scopes_encrypted: ScopesEncryptedEnvelope;
|
|
166
167
|
};
|
|
167
168
|
|
|
168
|
-
|
|
169
|
+
interface ScopeBearingDelegation {
|
|
170
|
+
scopes?: string[];
|
|
171
|
+
scopes_encrypted?: ScopesEncryptedEnvelope;
|
|
172
|
+
}
|
|
173
|
+
declare class ScopeNotGrantedError extends Error {
|
|
174
|
+
readonly reason: 'not_subscope' | 'scopes_encrypted' | 'no_scopes';
|
|
175
|
+
constructor(reason: ScopeNotGrantedError['reason'], message: string);
|
|
176
|
+
}
|
|
177
|
+
declare function assertScopeGranted(delegation: ScopeBearingDelegation, scopeExercised: string, fnName: string): void;
|
|
178
|
+
|
|
179
|
+
export { ActionEnvelope, ActorRef, AgentError, AgentErrorCode, ChainLink, DEFAULT_MAX_CHAIN_DEPTH, DelegationBond, DelegationEnvelope, DelegationRevocationRef, type FederationDelegationEnvelope, type FederationDescriptor, type FederationGuardian, type FederationPrincipal, type FederationRevocationEnvelope, type FederationSignature, type FederationVerifyResult, RevocationEnvelope, type ScopeBearingDelegation, ScopeNotGrantedError, ScopesEncryptedEnvelope, type SealScopesInput, SubdelegationEnvelope, type UnsealScopesInput, type UnsealedScopes, ValidationOptions, type VerifyActionInput, VerifyActionResult, type VerifyBase, type VerifyDelegationInput, VerifyDelegationResult, type VerifyFederationBase, type VerifyFederationDelegationInput, type VerifyFederationRevocationInput, type VerifyRevocationInput, VerifyRevocationResult, type VerifySubdelegationInput, VerifySubdelegationResult, assertScopeGranted, computeFederationDescriptorId, decodeScopesPayload, encodeScopesPayload, federationDescriptorCanonicalMessage, hasPrivateScopes, sealScopes, unsealScopes, verifyAction, verifyDelegation, verifyFederationDelegation, verifyFederationRevocation, verifyRevocation, verifySubdelegation };
|
package/dist/index.js
CHANGED
|
@@ -497,6 +497,25 @@ async function verifyDelegation(input) {
|
|
|
497
497
|
const ok = await input.verifyBip322(env.id, env.sig.value, env.principal.address);
|
|
498
498
|
if (!ok) return err("E_BAD_SIG", "BIP-322 signature did not verify");
|
|
499
499
|
}
|
|
500
|
+
if (!input.skipRevocationCheck && !input.revocations) {
|
|
501
|
+
return err(
|
|
502
|
+
"E_MALFORMED",
|
|
503
|
+
"verifyDelegation requires `revocations` (fetch kind-30085 by #delegation) or an explicit skipRevocationCheck:true to state that revocation is not being checked"
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
if (input.revocations) {
|
|
507
|
+
for (const rev of input.revocations) {
|
|
508
|
+
if (rev.delegation_id !== env.id) continue;
|
|
509
|
+
const rr = await verifyRevocation({
|
|
510
|
+
envelope: rev,
|
|
511
|
+
delegation: env,
|
|
512
|
+
verifyBip322: input.verifyBip322,
|
|
513
|
+
skipSignatureVerification: input.skipSignatureVerification
|
|
514
|
+
});
|
|
515
|
+
if (!rr.ok) continue;
|
|
516
|
+
return err("E_REVOKED", `delegation ${env.id} was revoked by ${rev.id}`);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
500
519
|
if (!input.skipTemporalCheck) {
|
|
501
520
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
502
521
|
const issued = new Date(env.issued_at);
|
|
@@ -531,6 +550,12 @@ async function verifyAction(input) {
|
|
|
531
550
|
scopeMode: input.scopeMode,
|
|
532
551
|
skipTemporalCheck: true,
|
|
533
552
|
// action window check dominates
|
|
553
|
+
// Revocation is evaluated ONCE, below, across every link in the chain
|
|
554
|
+
// (root + subdelegations) against the action's effective time — a
|
|
555
|
+
// per-link check here would use the wrong clock and would also
|
|
556
|
+
// double-report. Forward the caller's decision so this inner call does
|
|
557
|
+
// not trip the fail-closed guard.
|
|
558
|
+
skipRevocationCheck: true,
|
|
534
559
|
...input.decryptScopesWith ? { decryptScopesWith: input.decryptScopesWith } : {}
|
|
535
560
|
});
|
|
536
561
|
if (!dr.ok) return dr;
|
|
@@ -600,6 +625,12 @@ async function verifyAction(input) {
|
|
|
600
625
|
return err("E_BAD_SCOPE_GRAMMAR", msg);
|
|
601
626
|
}
|
|
602
627
|
if (!accepted) return err("E_SCOPE_DENIED", `scope_exercised (${exercised}) not a sub-scope of any granted scope`);
|
|
628
|
+
if (!input.skipRevocationCheck && !input.revocations) {
|
|
629
|
+
return err(
|
|
630
|
+
"E_MALFORMED",
|
|
631
|
+
"verifyAction requires `revocations` (fetch kind-30085 by #delegation for every link in the chain) or an explicit skipRevocationCheck:true"
|
|
632
|
+
);
|
|
633
|
+
}
|
|
603
634
|
if (input.revocations && input.revocations.length > 0) {
|
|
604
635
|
const allLinks = [rootHydrated, ...hydratedChain];
|
|
605
636
|
for (const link of allLinks) {
|
|
@@ -1102,6 +1133,37 @@ function fail(code, message) {
|
|
|
1102
1133
|
return { ok: false, code, message };
|
|
1103
1134
|
}
|
|
1104
1135
|
|
|
1136
|
+
// src/assert-scope.ts
|
|
1137
|
+
var ScopeNotGrantedError = class extends Error {
|
|
1138
|
+
constructor(reason, message) {
|
|
1139
|
+
super(message);
|
|
1140
|
+
this.name = "ScopeNotGrantedError";
|
|
1141
|
+
this.reason = reason;
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
function assertScopeGranted(delegation, scopeExercised, fnName) {
|
|
1145
|
+
if (hasPrivateScopes(delegation)) {
|
|
1146
|
+
throw new ScopeNotGrantedError(
|
|
1147
|
+
"scopes_encrypted",
|
|
1148
|
+
`${fnName}: this delegation uses v1.2 private scopes \u2014 \`scopes_encrypted\` is set and \`scopes\` is absent, so the granted set cannot be read here. Decrypt it with decryptPrivateScopes() using a matching device key and pass the recovered scopes as \`delegation.scopes\`. Refusing to stamp \`${scopeExercised}\` against an unreadable grant.`
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
const scopes = delegation.scopes;
|
|
1152
|
+
if (scopes === void 0 || scopes.length === 0) {
|
|
1153
|
+
throw new ScopeNotGrantedError(
|
|
1154
|
+
"no_scopes",
|
|
1155
|
+
`${fnName}: delegation grants no scopes, so \`${scopeExercised}\` cannot be exercised. An absent or empty \`scopes\` means nothing is granted, never everything.`
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
const exercised = parseScope(scopeExercised);
|
|
1159
|
+
if (!scopes.map(parseScope).some((g) => isSubScope(exercised, g))) {
|
|
1160
|
+
throw new ScopeNotGrantedError(
|
|
1161
|
+
"not_subscope",
|
|
1162
|
+
`${fnName}: scope_exercised (${scopeExercised}) is not a sub-scope of any granted scope (${scopes.join(", ")})`
|
|
1163
|
+
);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1105
1167
|
Object.defineProperty(exports, "canonicalize", {
|
|
1106
1168
|
enumerable: true,
|
|
1107
1169
|
get: function () { return canonical.canonicalize; }
|
|
@@ -1114,9 +1176,11 @@ exports.AgentError = AgentError;
|
|
|
1114
1176
|
exports.DEFAULT_MAX_CHAIN_DEPTH = DEFAULT_MAX_CHAIN_DEPTH;
|
|
1115
1177
|
exports.ENVELOPE_VERSION = ENVELOPE_VERSION;
|
|
1116
1178
|
exports.REGISTERED_SCOPES = REGISTERED_SCOPES;
|
|
1179
|
+
exports.ScopeNotGrantedError = ScopeNotGrantedError;
|
|
1117
1180
|
exports.ScopeParseError = ScopeParseError;
|
|
1118
1181
|
exports.actionCanonicalBytes = actionCanonicalBytes;
|
|
1119
1182
|
exports.actionCanonicalMessage = actionCanonicalMessage;
|
|
1183
|
+
exports.assertScopeGranted = assertScopeGranted;
|
|
1120
1184
|
exports.canonicalActionBytes = canonicalActionBytes;
|
|
1121
1185
|
exports.canonicalDelegationBytes = canonicalDelegationBytes;
|
|
1122
1186
|
exports.canonicalRevocationBytes = canonicalRevocationBytes;
|