agent-passport-system 2.1.0 → 2.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.
Files changed (33) hide show
  1. package/dist/src/adapters/mutual-auth-a2a.d.ts +53 -0
  2. package/dist/src/adapters/mutual-auth-a2a.d.ts.map +1 -0
  3. package/dist/src/adapters/mutual-auth-a2a.js +108 -0
  4. package/dist/src/adapters/mutual-auth-a2a.js.map +1 -0
  5. package/dist/src/adapters/mutual-auth-mcp.d.ts +55 -0
  6. package/dist/src/adapters/mutual-auth-mcp.d.ts.map +1 -0
  7. package/dist/src/adapters/mutual-auth-mcp.js +107 -0
  8. package/dist/src/adapters/mutual-auth-mcp.js.map +1 -0
  9. package/dist/src/index.d.ts +6 -0
  10. package/dist/src/index.d.ts.map +1 -1
  11. package/dist/src/index.js +4 -0
  12. package/dist/src/index.js.map +1 -1
  13. package/dist/src/v2/mutual-auth/certificate.d.ts +39 -0
  14. package/dist/src/v2/mutual-auth/certificate.d.ts.map +1 -0
  15. package/dist/src/v2/mutual-auth/certificate.js +89 -0
  16. package/dist/src/v2/mutual-auth/certificate.js.map +1 -0
  17. package/dist/src/v2/mutual-auth/handshake.d.ts +37 -0
  18. package/dist/src/v2/mutual-auth/handshake.d.ts.map +1 -0
  19. package/dist/src/v2/mutual-auth/handshake.js +216 -0
  20. package/dist/src/v2/mutual-auth/handshake.js.map +1 -0
  21. package/dist/src/v2/mutual-auth/index.d.ts +8 -0
  22. package/dist/src/v2/mutual-auth/index.d.ts.map +1 -0
  23. package/dist/src/v2/mutual-auth/index.js +8 -0
  24. package/dist/src/v2/mutual-auth/index.js.map +1 -0
  25. package/dist/src/v2/mutual-auth/trust-bundle.d.ts +20 -0
  26. package/dist/src/v2/mutual-auth/trust-bundle.d.ts.map +1 -0
  27. package/dist/src/v2/mutual-auth/trust-bundle.js +45 -0
  28. package/dist/src/v2/mutual-auth/trust-bundle.js.map +1 -0
  29. package/dist/src/v2/mutual-auth/types.d.ts +145 -0
  30. package/dist/src/v2/mutual-auth/types.d.ts.map +1 -0
  31. package/dist/src/v2/mutual-auth/types.js +19 -0
  32. package/dist/src/v2/mutual-auth/types.js.map +1 -0
  33. package/package.json +3 -3
@@ -0,0 +1,89 @@
1
+ // Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2
+ // ══════════════════════════════════════════════════════════════════
3
+ // Mutual Authentication v1 — certificate build, sign, verify
4
+ // ══════════════════════════════════════════════════════════════════
5
+ import { createHash } from 'node:crypto';
6
+ import { canonicalizeJCS } from '../../core/canonical-jcs.js';
7
+ import { sign as edSignHex, verify as edVerifyHex } from '../../crypto/keys.js';
8
+ const SPEC_VERSION = '1.0';
9
+ /** Build an unsigned certificate. Call signCertificate next. */
10
+ export function buildCertificate(input, issuer_pubkey_hex) {
11
+ return {
12
+ spec_version: SPEC_VERSION,
13
+ role: input.role,
14
+ subject_id: input.subject_id,
15
+ issuer_id: input.issuer_id,
16
+ issuer_role: input.issuer_role,
17
+ issuer_pubkey_hex,
18
+ subject_pubkey_hex: input.subject_pubkey_hex,
19
+ not_before: input.not_before,
20
+ not_after: input.not_after,
21
+ binding: input.binding,
22
+ attestation_grade: input.attestation_grade,
23
+ supported_versions: input.supported_versions,
24
+ capabilities: input.capabilities,
25
+ };
26
+ }
27
+ /** Sign an unsigned certificate with the issuer's private key (hex). */
28
+ export function signCertificate(unsigned, issuer_sk_hex) {
29
+ const canonical = canonicalizeJCS(unsigned);
30
+ const sig_hex = edSignHex(canonical, issuer_sk_hex);
31
+ const sig_b64 = Buffer.from(sig_hex, 'hex').toString('base64');
32
+ return { ...unsigned, signature_b64: sig_b64 };
33
+ }
34
+ /** Stable content-hash identifier for a certificate (for session_id
35
+ * derivation, audit references, etc.). Does not include the signature
36
+ * so equivalent unsigned certificates produce the same id. */
37
+ export function certificateId(cert) {
38
+ const { signature_b64: _sig, ...rest } = cert;
39
+ const canonical = canonicalizeJCS(rest);
40
+ return 'sha256:' + createHash('sha256').update(canonical).digest('hex');
41
+ }
42
+ export function verifyCertificateSignature(cert) {
43
+ if (!cert.supported_versions || cert.supported_versions.length === 0) {
44
+ return { ok: false, reason: 'version_empty' };
45
+ }
46
+ const { signature_b64, ...rest } = cert;
47
+ const canonical = canonicalizeJCS(rest);
48
+ const sig_hex = Buffer.from(signature_b64, 'base64').toString('hex');
49
+ const ok = edVerifyHex(canonical, sig_hex, cert.issuer_pubkey_hex);
50
+ if (!ok)
51
+ return { ok: false, reason: 'signature_invalid' };
52
+ return { ok: true };
53
+ }
54
+ /** Check validity window using a supplied now() (unix ms). */
55
+ export function isCertificateTemporallyValid(cert, now_ms, max_clock_skew_ms = 0) {
56
+ if (now_ms + max_clock_skew_ms < cert.not_before) {
57
+ return { ok: false, reason: 'not_yet_valid' };
58
+ }
59
+ if (now_ms - max_clock_skew_ms > cert.not_after) {
60
+ return { ok: false, reason: 'expired' };
61
+ }
62
+ return { ok: true };
63
+ }
64
+ /** Given a certificate and a local trust-anchor list, determine if
65
+ * the certificate was issued by a trusted anchor and whether the
66
+ * anchor's binding constraints (if any) permit this cert's binding. */
67
+ export function checkAnchor(cert, anchors, revoked_anchor_ids = []) {
68
+ const anchor = anchors.find((a) => a.pubkey_hex === cert.issuer_pubkey_hex);
69
+ if (!anchor)
70
+ return { ok: false, reason: 'unknown_issuer' };
71
+ if (revoked_anchor_ids.includes(anchor.anchor_id)) {
72
+ return { ok: false, anchor, reason: 'revoked_anchor' };
73
+ }
74
+ if (anchor.binding_constraints && anchor.binding_constraints.length > 0) {
75
+ const matched = anchor.binding_constraints.some((pat) => matchBinding(pat, cert.binding));
76
+ if (!matched)
77
+ return { ok: false, anchor, reason: 'binding_mismatch' };
78
+ }
79
+ return { ok: true, anchor };
80
+ }
81
+ function matchBinding(pattern, binding) {
82
+ if (pattern === binding)
83
+ return true;
84
+ if (pattern.endsWith('*')) {
85
+ return binding.startsWith(pattern.slice(0, -1));
86
+ }
87
+ return false;
88
+ }
89
+ //# sourceMappingURL=certificate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"certificate.js","sourceRoot":"","sources":["../../../../src/v2/mutual-auth/certificate.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,qEAAqE;AACrE,6DAA6D;AAC7D,qEAAqE;AAErE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAO/E,MAAM,YAAY,GAAG,KAAc,CAAA;AAkBnC,gEAAgE;AAChE,MAAM,UAAU,gBAAgB,CAC9B,KAA4B,EAC5B,iBAAyB;IAEzB,OAAO;QACL,YAAY,EAAE,YAAY;QAC1B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,iBAAiB;QACjB,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,YAAY,EAAE,KAAK,CAAC,YAAY;KACjC,CAAA;AACH,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,eAAe,CAC7B,QAAsD,EACtD,aAAqB;IAErB,MAAM,SAAS,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;IAC3C,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;IACnD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAC9D,OAAO,EAAE,GAAG,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,CAAA;AAChD,CAAC;AAED;;+DAE+D;AAC/D,MAAM,UAAU,aAAa,CAAC,IAA2B;IACvD,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAA;IAC7C,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;IACvC,OAAO,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACzE,CAAC;AASD,MAAM,UAAU,0BAA0B,CACxC,IAA2B;IAE3B,IAAI,CAAC,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAA;IAC/C,CAAC;IACD,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAA;IACvC,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IACpE,MAAM,EAAE,GAAG,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAA;IAClE,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAA;IAC1D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;AACrB,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,4BAA4B,CAC1C,IAA2B,EAC3B,MAAc,EACd,iBAAiB,GAAG,CAAC;IAErB,IAAI,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QACjD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAA;IAC/C,CAAC;IACD,IAAI,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;IACzC,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;AACrB,CAAC;AAUD;;wEAEwE;AACxE,MAAM,UAAU,WAAW,CACzB,IAA2B,EAC3B,OAAsB,EACtB,qBAA+B,EAAE;IAEjC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,iBAAiB,CAAC,CAAA;IAC3E,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAA;IAC3D,IAAI,kBAAkB,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAClD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAA;IACxD,CAAC;IACD,IAAI,MAAM,CAAC,mBAAmB,IAAI,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxE,MAAM,OAAO,GAAG,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CACtD,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAChC,CAAA;QACD,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAA;IACxE,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;AAC7B,CAAC;AAED,SAAS,YAAY,CAAC,OAAe,EAAE,OAAe;IACpD,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,IAAI,CAAA;IACpC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IACjD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { MutualAuthAttest, MutualAuthCertificate, MutualAuthFailureReason, MutualAuthHello, MutualAuthPolicy, MutualAuthResult, MutualAuthRole, MutualAuthSession, TrustAnchor } from './types.js';
2
+ export declare function newNonce(): string;
3
+ export declare function buildHello(role: MutualAuthRole, supported_versions: string[], now_ms: number, nonce_b64?: string): MutualAuthHello;
4
+ /** Choose the highest mutually supported version. Returns null if
5
+ * there is no overlap. Both sides MUST run the same algorithm. */
6
+ export declare function chooseVersion(peer_supported: string[], own_accepted: string[]): string | null;
7
+ export interface BuildAttestInput {
8
+ role: MutualAuthRole;
9
+ chosen_version: string;
10
+ own_nonce_b64: string;
11
+ peer_nonce_b64: string;
12
+ certificate: MutualAuthCertificate;
13
+ now_ms: number;
14
+ }
15
+ export declare function buildAttest(input: BuildAttestInput, own_sk_hex: string): MutualAuthAttest;
16
+ export interface VerifyAttestInput {
17
+ attest: MutualAuthAttest;
18
+ expected_peer_nonce_b64: string;
19
+ expected_own_nonce_b64: string;
20
+ policy: MutualAuthPolicy;
21
+ trust_anchors: TrustAnchor[];
22
+ revoked_anchor_ids?: string[];
23
+ now_ms: number;
24
+ }
25
+ export interface VerifyAttestOutcome {
26
+ ok: boolean;
27
+ reason?: MutualAuthFailureReason;
28
+ detail?: string;
29
+ }
30
+ export declare function verifyAttest(input: VerifyAttestInput): VerifyAttestOutcome;
31
+ /** Derive the shared session record from both sides' Attests. Both
32
+ * parties MUST compute identical session_id values given identical
33
+ * inputs (canonical JCS + sha256). */
34
+ export declare function deriveSession(agent_attest: MutualAuthAttest, is_attest: MutualAuthAttest, policy: MutualAuthPolicy, now_ms: number): MutualAuthResult;
35
+ /** Check whether a MutualAuthSession is still alive. */
36
+ export declare function isSessionActive(session: MutualAuthSession, now_ms: number): boolean;
37
+ //# sourceMappingURL=handshake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../../../../src/v2/mutual-auth/handshake.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EACV,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,WAAW,EACZ,MAAM,YAAY,CAAA;AAMnB,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAID,wBAAgB,UAAU,CACxB,IAAI,EAAE,cAAc,EACpB,kBAAkB,EAAE,MAAM,EAAE,EAC5B,MAAM,EAAE,MAAM,EACd,SAAS,SAAa,GACrB,eAAe,CAQjB;AAID;mEACmE;AACnE,wBAAgB,aAAa,CAC3B,cAAc,EAAE,MAAM,EAAE,EACxB,YAAY,EAAE,MAAM,EAAE,GACrB,MAAM,GAAG,IAAI,CAKf;AAID,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,cAAc,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,qBAAqB,CAAA;IAClC,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,gBAAgB,EACvB,UAAU,EAAE,MAAM,GACjB,gBAAgB,CAgBlB;AAID,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,gBAAgB,CAAA;IACxB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,sBAAsB,EAAE,MAAM,CAAA;IAC9B,MAAM,EAAE,gBAAgB,CAAA;IACxB,aAAa,EAAE,WAAW,EAAE,CAAA;IAC5B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,CAAC,EAAE,uBAAuB,CAAA;IAChC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,mBAAmB,CA6G1E;AAID;;uCAEuC;AACvC,wBAAgB,aAAa,CAC3B,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,gBAAgB,EAC3B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,GACb,gBAAgB,CAsDlB;AAED,wDAAwD;AACxD,wBAAgB,eAAe,CAC7B,OAAO,EAAE,iBAAiB,EAC1B,MAAM,EAAE,MAAM,GACb,OAAO,CAOT"}
@@ -0,0 +1,216 @@
1
+ // Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2
+ // ══════════════════════════════════════════════════════════════════
3
+ // Mutual Authentication v1 — handshake primitives
4
+ // ══════════════════════════════════════════════════════════════════
5
+ // Flow:
6
+ // 1. Initiator sends MutualAuthHello (nonce_i, supported_versions)
7
+ // 2. Responder replies with MutualAuthAttest (chosen_version, own
8
+ // nonce_r, peer nonce_i, own certificate, signed commitment
9
+ // over all four).
10
+ // 3. Initiator verifies the Attest, then replies with its own
11
+ // MutualAuthAttest (chosen_version must match, nonces swapped).
12
+ // 4. Responder verifies the initiator's Attest.
13
+ // 5. Both sides derive an identical MutualAuthSession (shared
14
+ // hash of the two Attests → session_id).
15
+ //
16
+ // Downgrade defence:
17
+ // The Attest signature commits to the chosen_version alongside
18
+ // both nonces and both certs. An attacker who can tamper with
19
+ // supported_versions at the transport layer cannot forge a valid
20
+ // Attest that advertises a lower version without also breaking
21
+ // the signature.
22
+ //
23
+ // Replay defence:
24
+ // The nonces are 128-bit random, the Attest timestamp is bounded
25
+ // by max_clock_skew_ms, and the session_id derivation includes
26
+ // both nonces.
27
+ // ══════════════════════════════════════════════════════════════════
28
+ import { createHash, randomBytes } from 'node:crypto';
29
+ import { canonicalizeJCS } from '../../core/canonical-jcs.js';
30
+ import { sign as edSignHex, verify as edVerifyHex } from '../../crypto/keys.js';
31
+ import { certificateId, checkAnchor, isCertificateTemporallyValid, verifyCertificateSignature, } from './certificate.js';
32
+ const SPEC_VERSION = '1.0';
33
+ // ── Nonce generation ──
34
+ export function newNonce() {
35
+ return randomBytes(16).toString('base64');
36
+ }
37
+ // ── Hello ──
38
+ export function buildHello(role, supported_versions, now_ms, nonce_b64 = newNonce()) {
39
+ return {
40
+ spec_version: SPEC_VERSION,
41
+ role,
42
+ supported_versions,
43
+ nonce_b64,
44
+ timestamp: now_ms,
45
+ };
46
+ }
47
+ // ── Version negotiation ──
48
+ /** Choose the highest mutually supported version. Returns null if
49
+ * there is no overlap. Both sides MUST run the same algorithm. */
50
+ export function chooseVersion(peer_supported, own_accepted) {
51
+ for (const v of own_accepted) {
52
+ if (peer_supported.includes(v))
53
+ return v;
54
+ }
55
+ return null;
56
+ }
57
+ export function buildAttest(input, own_sk_hex) {
58
+ const unsigned = {
59
+ spec_version: SPEC_VERSION,
60
+ role: input.role,
61
+ chosen_version: input.chosen_version,
62
+ own_nonce_b64: input.own_nonce_b64,
63
+ peer_nonce_b64: input.peer_nonce_b64,
64
+ certificate: input.certificate,
65
+ timestamp: input.now_ms,
66
+ };
67
+ const canonical = canonicalizeJCS(unsigned);
68
+ const sig_hex = edSignHex(canonical, own_sk_hex);
69
+ return {
70
+ ...unsigned,
71
+ signature_b64: Buffer.from(sig_hex, 'hex').toString('base64'),
72
+ };
73
+ }
74
+ export function verifyAttest(input) {
75
+ const { attest, policy, trust_anchors, now_ms } = input;
76
+ const skew = policy.max_clock_skew_ms ?? 0;
77
+ // 1. Version negotiated must be one we accept
78
+ if (!policy.accepted_versions.includes(attest.chosen_version)) {
79
+ return { ok: false, reason: 'version_unsupported' };
80
+ }
81
+ // 2. Nonces must match what we expect (replay + mitm defence)
82
+ if (attest.peer_nonce_b64 !== input.expected_peer_nonce_b64) {
83
+ return { ok: false, reason: 'nonce_mismatch', detail: 'peer_nonce' };
84
+ }
85
+ if (attest.own_nonce_b64 !== input.expected_own_nonce_b64) {
86
+ return { ok: false, reason: 'nonce_mismatch', detail: 'own_nonce' };
87
+ }
88
+ // 3. Timestamp must be within clock skew
89
+ if (Math.abs(now_ms - attest.timestamp) > Math.max(skew, 60_000)) {
90
+ return { ok: false, reason: 'replay_detected', detail: 'timestamp_skew' };
91
+ }
92
+ // 4. Embedded certificate must be temporally valid
93
+ const temporal = isCertificateTemporallyValid(attest.certificate, now_ms, skew);
94
+ if (!temporal.ok) {
95
+ return {
96
+ ok: false,
97
+ reason: temporal.reason === 'expired'
98
+ ? 'expired_certificate'
99
+ : temporal.reason === 'not_yet_valid'
100
+ ? 'not_yet_valid_certificate'
101
+ : 'signature_invalid',
102
+ };
103
+ }
104
+ // 5. Certificate signature must verify
105
+ const certSig = verifyCertificateSignature(attest.certificate);
106
+ if (!certSig.ok) {
107
+ return { ok: false, reason: 'signature_invalid', detail: 'certificate' };
108
+ }
109
+ // 6. Certificate must be vouched for by a known trust anchor
110
+ const anchor = checkAnchor(attest.certificate, trust_anchors, input.revoked_anchor_ids);
111
+ if (!anchor.ok) {
112
+ return {
113
+ ok: false,
114
+ reason: anchor.reason === 'unknown_issuer'
115
+ ? 'unknown_issuer'
116
+ : anchor.reason === 'revoked_anchor'
117
+ ? 'revoked_anchor'
118
+ : 'binding_mismatch',
119
+ };
120
+ }
121
+ // 7. Downgrade detection: the chosen_version MUST be the highest
122
+ // we and the peer both support. If peer's supported_versions in
123
+ // the embedded cert offers something higher that we also accept,
124
+ // a downgrade was forced.
125
+ const peerSupported = attest.certificate.supported_versions;
126
+ const expectedChoice = chooseVersion(peerSupported, policy.accepted_versions);
127
+ if (expectedChoice !== null && expectedChoice !== attest.chosen_version) {
128
+ return { ok: false, reason: 'downgrade_detected' };
129
+ }
130
+ // 8. Agent grade policy (only applies when peer is an agent)
131
+ if (attest.certificate.role === 'agent' && policy.min_agent_grade !== undefined) {
132
+ const grade = attest.certificate.attestation_grade ?? 0;
133
+ if (grade < policy.min_agent_grade) {
134
+ return { ok: false, reason: 'grade_insufficient' };
135
+ }
136
+ }
137
+ // 9. Required capabilities (policy check)
138
+ if (policy.required_capabilities && policy.required_capabilities.length > 0) {
139
+ const caps = attest.certificate.capabilities ?? [];
140
+ for (const required of policy.required_capabilities) {
141
+ if (!caps.includes(required)) {
142
+ return {
143
+ ok: false,
144
+ reason: 'binding_mismatch',
145
+ detail: `missing_capability:${required}`,
146
+ };
147
+ }
148
+ }
149
+ }
150
+ // 10. Verify the attest signature itself (commits to chosen_version
151
+ // + both nonces + cert — this is the downgrade defence).
152
+ const { signature_b64, ...rest } = attest;
153
+ const canonical = canonicalizeJCS(rest);
154
+ const sig_hex = Buffer.from(signature_b64, 'base64').toString('hex');
155
+ const sigOk = edVerifyHex(canonical, sig_hex, attest.certificate.subject_pubkey_hex);
156
+ if (!sigOk)
157
+ return { ok: false, reason: 'signature_invalid', detail: 'attest' };
158
+ return { ok: true };
159
+ }
160
+ // ── Session derivation ──
161
+ /** Derive the shared session record from both sides' Attests. Both
162
+ * parties MUST compute identical session_id values given identical
163
+ * inputs (canonical JCS + sha256). */
164
+ export function deriveSession(agent_attest, is_attest, policy, now_ms) {
165
+ if (agent_attest.chosen_version !== is_attest.chosen_version) {
166
+ return { ok: false, failure: { reason: 'downgrade_detected' } };
167
+ }
168
+ if (agent_attest.certificate.role !== 'agent') {
169
+ return {
170
+ ok: false,
171
+ failure: { reason: 'binding_mismatch', detail: 'agent_attest_role' },
172
+ };
173
+ }
174
+ if (is_attest.certificate.role !== 'information_system') {
175
+ return {
176
+ ok: false,
177
+ failure: { reason: 'binding_mismatch', detail: 'is_attest_role' },
178
+ };
179
+ }
180
+ const agent_cert_id = certificateId(agent_attest.certificate);
181
+ const is_cert_id = certificateId(is_attest.certificate);
182
+ const sessionMaterial = canonicalizeJCS({
183
+ spec_version: SPEC_VERSION,
184
+ chosen_version: agent_attest.chosen_version,
185
+ agent_cert_id,
186
+ is_cert_id,
187
+ agent_nonce_b64: agent_attest.own_nonce_b64,
188
+ is_nonce_b64: is_attest.own_nonce_b64,
189
+ });
190
+ const session_id = 'sha256:' + createHash('sha256').update(sessionMaterial).digest('hex');
191
+ const max_session = policy.max_session_ms ??
192
+ Math.min(agent_attest.certificate.not_after, is_attest.certificate.not_after) - now_ms;
193
+ const session = {
194
+ spec_version: SPEC_VERSION,
195
+ session_id,
196
+ agent_cert: agent_attest.certificate,
197
+ is_cert: is_attest.certificate,
198
+ chosen_version: agent_attest.chosen_version,
199
+ agent_nonce_b64: agent_attest.own_nonce_b64,
200
+ is_nonce_b64: is_attest.own_nonce_b64,
201
+ established_at: now_ms,
202
+ expires_at: now_ms + Math.max(0, max_session),
203
+ };
204
+ if (session.expires_at <= session.established_at) {
205
+ return { ok: false, failure: { reason: 'expired_session' } };
206
+ }
207
+ return { ok: true, session };
208
+ }
209
+ /** Check whether a MutualAuthSession is still alive. */
210
+ export function isSessionActive(session, now_ms) {
211
+ return (now_ms >= session.established_at &&
212
+ now_ms <= session.expires_at &&
213
+ now_ms <= session.agent_cert.not_after &&
214
+ now_ms <= session.is_cert.not_after);
215
+ }
216
+ //# sourceMappingURL=handshake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../../../src/v2/mutual-auth/handshake.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export type { MutualAuthRole, MutualAuthCertificate, MutualAuthHello, MutualAuthAttest, MutualAuthSession, MutualAuthResult, MutualAuthPolicy, MutualAuthFailureReason, TrustAnchor, TrustAnchorBundle, AgentCertBinding, } from './types.js';
2
+ export { buildCertificate, signCertificate, certificateId, verifyCertificateSignature, isCertificateTemporallyValid, checkAnchor, } from './certificate.js';
3
+ export type { BuildCertificateInput, VerifyCertificateOutcome, AnchorCheckOutcome, } from './certificate.js';
4
+ export { buildBundle, signBundle, verifyBundle, } from './trust-bundle.js';
5
+ export type { BuildBundleInput, BundleVerifyOutcome, BundleVerifyReason, } from './trust-bundle.js';
6
+ export { newNonce, buildHello, chooseVersion, buildAttest, verifyAttest, deriveSession, isSessionActive, } from './handshake.js';
7
+ export type { BuildAttestInput, VerifyAttestInput, VerifyAttestOutcome, } from './handshake.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/v2/mutual-auth/index.ts"],"names":[],"mappings":"AAKA,YAAY,EACV,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,WAAW,EACX,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,0BAA0B,EAC1B,4BAA4B,EAC5B,WAAW,GACZ,MAAM,kBAAkB,CAAA;AAEzB,YAAY,EACV,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,WAAW,EACX,UAAU,EACV,YAAY,GACb,MAAM,mBAAmB,CAAA;AAE1B,YAAY,EACV,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,WAAW,EACX,YAAY,EACZ,aAAa,EACb,eAAe,GAChB,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,8 @@
1
+ // Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2
+ // ══════════════════════════════════════════════════════════════════
3
+ // Mutual Authentication v1 — module surface
4
+ // ══════════════════════════════════════════════════════════════════
5
+ export { buildCertificate, signCertificate, certificateId, verifyCertificateSignature, isCertificateTemporallyValid, checkAnchor, } from './certificate.js';
6
+ export { buildBundle, signBundle, verifyBundle, } from './trust-bundle.js';
7
+ export { newNonce, buildHello, chooseVersion, buildAttest, verifyAttest, deriveSession, isSessionActive, } from './handshake.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/v2/mutual-auth/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,qEAAqE;AACrE,4CAA4C;AAC5C,qEAAqE;AAgBrE,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,0BAA0B,EAC1B,4BAA4B,EAC5B,WAAW,GACZ,MAAM,kBAAkB,CAAA;AAQzB,OAAO,EACL,WAAW,EACX,UAAU,EACV,YAAY,GACb,MAAM,mBAAmB,CAAA;AAQ1B,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,WAAW,EACX,YAAY,EACZ,aAAa,EACb,eAAe,GAChB,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,20 @@
1
+ import type { TrustAnchorBundle, TrustAnchor } from './types.js';
2
+ export interface BuildBundleInput {
3
+ bundle_id: string;
4
+ anchors: TrustAnchor[];
5
+ issued_at: number;
6
+ refresh_after: number;
7
+ revoked_anchors?: string[];
8
+ }
9
+ export declare function buildBundle(input: BuildBundleInput, publisher_pubkey_hex: string): Omit<TrustAnchorBundle, 'signature_b64'>;
10
+ export declare function signBundle(unsigned: Omit<TrustAnchorBundle, 'signature_b64'>, publisher_sk_hex: string): TrustAnchorBundle;
11
+ export type BundleVerifyReason = 'signature_invalid' | 'untrusted_publisher' | 'bundle_expired' | 'not_yet_valid';
12
+ export interface BundleVerifyOutcome {
13
+ ok: boolean;
14
+ reason?: BundleVerifyReason;
15
+ }
16
+ /** Verify a bundle's signature and freshness. The verifier supplies
17
+ * a root-trusted publisher key list; the bundle MUST be signed by
18
+ * one of them, not merely self-signed. */
19
+ export declare function verifyBundle(bundle: TrustAnchorBundle, trusted_publisher_pubkeys_hex: string[], now_ms: number): BundleVerifyOutcome;
20
+ //# sourceMappingURL=trust-bundle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-bundle.d.ts","sourceRoot":"","sources":["../../../../src/v2/mutual-auth/trust-bundle.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAMhE,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,gBAAgB,EACvB,oBAAoB,EAAE,MAAM,GAC3B,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,CAU1C;AAED,wBAAgB,UAAU,CACxB,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,EAClD,gBAAgB,EAAE,MAAM,GACvB,iBAAiB,CAKnB;AAID,MAAM,MAAM,kBAAkB,GAC1B,mBAAmB,GACnB,qBAAqB,GACrB,gBAAgB,GAChB,eAAe,CAAA;AAEnB,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAC5B;AAED;;2CAE2C;AAC3C,wBAAgB,YAAY,CAC1B,MAAM,EAAE,iBAAiB,EACzB,6BAA6B,EAAE,MAAM,EAAE,EACvC,MAAM,EAAE,MAAM,GACb,mBAAmB,CAcrB"}
@@ -0,0 +1,45 @@
1
+ // Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2
+ // ══════════════════════════════════════════════════════════════════
3
+ // Mutual Authentication v1 — trust anchor bundle build, sign, verify
4
+ // ══════════════════════════════════════════════════════════════════
5
+ import { canonicalizeJCS } from '../../core/canonical-jcs.js';
6
+ import { sign as edSignHex, verify as edVerifyHex } from '../../crypto/keys.js';
7
+ const SPEC_VERSION = '1.0';
8
+ export function buildBundle(input, publisher_pubkey_hex) {
9
+ return {
10
+ spec_version: SPEC_VERSION,
11
+ bundle_id: input.bundle_id,
12
+ issued_at: input.issued_at,
13
+ anchors: input.anchors,
14
+ refresh_after: input.refresh_after,
15
+ revoked_anchors: input.revoked_anchors,
16
+ publisher_pubkey_hex,
17
+ };
18
+ }
19
+ export function signBundle(unsigned, publisher_sk_hex) {
20
+ const canonical = canonicalizeJCS(unsigned);
21
+ const sig_hex = edSignHex(canonical, publisher_sk_hex);
22
+ const sig_b64 = Buffer.from(sig_hex, 'hex').toString('base64');
23
+ return { ...unsigned, signature_b64: sig_b64 };
24
+ }
25
+ /** Verify a bundle's signature and freshness. The verifier supplies
26
+ * a root-trusted publisher key list; the bundle MUST be signed by
27
+ * one of them, not merely self-signed. */
28
+ export function verifyBundle(bundle, trusted_publisher_pubkeys_hex, now_ms) {
29
+ if (!trusted_publisher_pubkeys_hex.includes(bundle.publisher_pubkey_hex)) {
30
+ return { ok: false, reason: 'untrusted_publisher' };
31
+ }
32
+ const { signature_b64, ...rest } = bundle;
33
+ const canonical = canonicalizeJCS(rest);
34
+ const sig_hex = Buffer.from(signature_b64, 'base64').toString('hex');
35
+ const ok = edVerifyHex(canonical, sig_hex, bundle.publisher_pubkey_hex);
36
+ if (!ok)
37
+ return { ok: false, reason: 'signature_invalid' };
38
+ if (now_ms < bundle.issued_at)
39
+ return { ok: false, reason: 'not_yet_valid' };
40
+ if (now_ms > bundle.refresh_after) {
41
+ return { ok: false, reason: 'bundle_expired' };
42
+ }
43
+ return { ok: true };
44
+ }
45
+ //# sourceMappingURL=trust-bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-bundle.js","sourceRoot":"","sources":["../../../../src/v2/mutual-auth/trust-bundle.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,qEAAqE;AACrE,qEAAqE;AACrE,qEAAqE;AAErE,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAG/E,MAAM,YAAY,GAAG,KAAc,CAAA;AAYnC,MAAM,UAAU,WAAW,CACzB,KAAuB,EACvB,oBAA4B;IAE5B,OAAO;QACL,YAAY,EAAE,YAAY;QAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,oBAAoB;KACrB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,QAAkD,EAClD,gBAAwB;IAExB,MAAM,SAAS,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;IAC3C,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA;IACtD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAC9D,OAAO,EAAE,GAAG,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,CAAA;AAChD,CAAC;AAeD;;2CAE2C;AAC3C,MAAM,UAAU,YAAY,CAC1B,MAAyB,EACzB,6BAAuC,EACvC,MAAc;IAEd,IAAI,CAAC,6BAA6B,CAAC,QAAQ,CAAC,MAAM,CAAC,oBAAoB,CAAC,EAAE,CAAC;QACzE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAA;IACrD,CAAC;IACD,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAA;IACzC,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IACpE,MAAM,EAAE,GAAG,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,oBAAoB,CAAC,CAAA;IACvE,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAA;IAC1D,IAAI,MAAM,GAAG,MAAM,CAAC,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAA;IAC5E,IAAI,MAAM,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC;QAClC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAA;IAChD,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;AACrB,CAAC"}
@@ -0,0 +1,145 @@
1
+ import type { SignedPassport, Delegation } from '../../types/passport.js';
2
+ /** Role of the party this certificate identifies. */
3
+ export type MutualAuthRole = 'agent' | 'information_system';
4
+ /** Minimal certificate carried by a party for mutual-auth handshake.
5
+ *
6
+ * For an agent, the certificate is a thin wrapper over the existing
7
+ * APS passport plus active delegation context. For an IS, it is a
8
+ * first-class shape this module defines: a signed statement that
9
+ * this endpoint is authorized to serve the named resource domain.
10
+ */
11
+ export interface MutualAuthCertificate {
12
+ spec_version: '1.0';
13
+ role: MutualAuthRole;
14
+ subject_id: string;
15
+ issuer_id: string;
16
+ issuer_role: MutualAuthRole | 'trust_anchor';
17
+ issuer_pubkey_hex: string;
18
+ subject_pubkey_hex: string;
19
+ /** Earliest time this cert is valid (unix ms). */
20
+ not_before: number;
21
+ /** Latest time this cert is valid (unix ms). */
22
+ not_after: number;
23
+ /** For agents: APS passport agent_id they are acting under.
24
+ * For IS: resource domain they are authorized to serve
25
+ * (e.g. "api.bank.example.com"). */
26
+ binding: string;
27
+ /** Optional APS attestation grade for an agent cert (0..3). */
28
+ attestation_grade?: 0 | 1 | 2 | 3;
29
+ /** Protocol versions the subject supports, highest first. Used for
30
+ * downgrade-attack detection during handshake. */
31
+ supported_versions: string[];
32
+ /** Optional list of session-level capabilities this cert grants.
33
+ * Treated as a set; absence means no capabilities. */
34
+ capabilities?: string[];
35
+ /** Ed25519 signature over the canonical form of this object with
36
+ * `signature_b64` omitted. */
37
+ signature_b64: string;
38
+ }
39
+ /** Self-contained trust anchor entry carried locally by a party.
40
+ * Local verification matches a certificate's `issuer_pubkey_hex`
41
+ * against this list. No network call is performed. */
42
+ export interface TrustAnchor {
43
+ anchor_id: string;
44
+ display_name: string;
45
+ role: MutualAuthRole | 'trust_anchor';
46
+ pubkey_hex: string;
47
+ not_before: number;
48
+ not_after: number;
49
+ /** Optional: constrain this anchor to vouch only for certs whose
50
+ * `binding` matches one of these patterns (glob, prefix, or
51
+ * exact). Empty/omitted means unconstrained. */
52
+ binding_constraints?: string[];
53
+ }
54
+ export interface TrustAnchorBundle {
55
+ spec_version: '1.0';
56
+ bundle_id: string;
57
+ issued_at: number;
58
+ anchors: TrustAnchor[];
59
+ /** Parties carrying this bundle refresh it at or before this time. */
60
+ refresh_after: number;
61
+ /** Optional: short-form revocation of specific anchor_ids that were
62
+ * valid in a prior bundle snapshot. */
63
+ revoked_anchors?: string[];
64
+ /** Signature over the canonical form with `signature_b64` omitted,
65
+ * by the bundle publisher's Ed25519 key. Verifiers MAY additionally
66
+ * require the bundle to be signed by a configured root key. */
67
+ signature_b64: string;
68
+ /** Publisher pubkey (hex). Must itself be trusted by root
69
+ * configuration, not by the bundle alone. */
70
+ publisher_pubkey_hex: string;
71
+ }
72
+ /** One nonce round of the two-way handshake. */
73
+ export interface MutualAuthHello {
74
+ spec_version: '1.0';
75
+ role: MutualAuthRole;
76
+ supported_versions: string[];
77
+ nonce_b64: string;
78
+ /** Timestamp (unix ms) the initiator produced the hello. */
79
+ timestamp: number;
80
+ }
81
+ /** Response a party returns after validating the counterparty's
82
+ * certificate. The signature commits the party to
83
+ * (chosen_version || peer_nonce_b64 || own_nonce_b64 || own_cert_id)
84
+ * which is the downgrade-attack defence. */
85
+ export interface MutualAuthAttest {
86
+ spec_version: '1.0';
87
+ role: MutualAuthRole;
88
+ chosen_version: string;
89
+ own_nonce_b64: string;
90
+ peer_nonce_b64: string;
91
+ certificate: MutualAuthCertificate;
92
+ /** Signature over the canonical form with `signature_b64` omitted.
93
+ * The canonical form MUST include chosen_version, both nonces,
94
+ * and the embedded certificate. */
95
+ signature_b64: string;
96
+ /** Timestamp (unix ms). */
97
+ timestamp: number;
98
+ }
99
+ export interface MutualAuthSession {
100
+ spec_version: '1.0';
101
+ /** Stable content-hash identifier for this session. */
102
+ session_id: string;
103
+ agent_cert: MutualAuthCertificate;
104
+ is_cert: MutualAuthCertificate;
105
+ chosen_version: string;
106
+ agent_nonce_b64: string;
107
+ is_nonce_b64: string;
108
+ established_at: number;
109
+ /** Session ends at or before this time; both sides enforce. */
110
+ expires_at: number;
111
+ }
112
+ export type MutualAuthFailureReason = 'expired_certificate' | 'not_yet_valid_certificate' | 'unknown_issuer' | 'revoked_anchor' | 'signature_invalid' | 'binding_mismatch' | 'downgrade_detected' | 'nonce_mismatch' | 'version_unsupported' | 'expired_session' | 'replay_detected' | 'grade_insufficient';
113
+ export interface MutualAuthResult {
114
+ ok: boolean;
115
+ session?: MutualAuthSession;
116
+ failure?: {
117
+ reason: MutualAuthFailureReason;
118
+ detail?: string;
119
+ };
120
+ }
121
+ /** Local trust policy a party applies when accepting a peer cert. */
122
+ export interface MutualAuthPolicy {
123
+ /** Minimum agent attestation grade (0..3). Only applies when the
124
+ * peer is an agent. */
125
+ min_agent_grade?: 0 | 1 | 2 | 3;
126
+ /** Protocol versions this party accepts, highest preference first. */
127
+ accepted_versions: string[];
128
+ /** Required capabilities the peer cert must carry. */
129
+ required_capabilities?: string[];
130
+ /** Maximum allowed clock skew (milliseconds) when comparing
131
+ * timestamps. */
132
+ max_clock_skew_ms?: number;
133
+ /** Maximum session lifetime accepted (milliseconds). */
134
+ max_session_ms?: number;
135
+ }
136
+ /** Signed envelope binding an APS passport + active delegation into
137
+ * a MutualAuthCertificate. This is how an agent carries its APS
138
+ * identity into the mutual-auth layer without changing the passport
139
+ * spec itself. */
140
+ export interface AgentCertBinding {
141
+ passport: SignedPassport;
142
+ delegation: Delegation;
143
+ certificate: MutualAuthCertificate;
144
+ }
145
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/v2/mutual-auth/types.ts"],"names":[],"mappings":"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"}