/** * Mutual session proof-of-possession. * * Single-sided `proof-of-possession.ts` proves *one* party holds their key * (a delegation holder answering a verifier's challenge). That's fine for * an asymmetric relationship — one side presents credentials, the other * checks them. But when two hearts establish a session with each other * (subtask dispatch, peer negotiation, A2A calls), both sides need to * authenticate. Otherwise the verifier-side of a one-sided PoP exchange * could itself be a man-in-the-middle: it learns who you are, you never * confirm who it is. * * Protocol — three-message handshake: * 1. A sends `SessionInit`: {sessionId, nonceA, initiatorDid, initiatorPublicKey, purpose}. * 2. B replies `SessionAccept`: init echoed, adds nonceB + responderDid + * responderPublicKey, signs canonical(init || accept_payload). * 3. A verifies B's signature, signs the *same transcript*, replies `SessionConfirm`. * 4. B verifies A's signature. * * After both signatures verify against the DIDs, each side has a proof * that the counterparty holds the key for the DID they advertised. The * `transcriptHash` is a stable SHA-256 both parties can bind into later * operations (receipts, heartbeats) so every follow-up message is * traceable to this specific session. * * Replay protection: nonces are 32 random bytes each, fresh per session. * The transcript includes `initiatedAt` (and optionally `ttlMs`) so * verifiers can reject stale handshakes. Sessions are one-shot: once * confirmed, the transcriptHash is the session identifier — don't reuse. */ import { type CryptoProvider } from '../core/crypto-provider.js'; import { type DidMethodRegistry } from '../core/did-method.js'; /** Initiator's opening message — includes their nonce + identity claim. */ export interface SessionInit { sessionId: string; initiatorDid: string; initiatorPublicKey: string; nonceA: string; purpose: string; initiatedAt: number; ttlMs: number | null; } /** Responder's reply — includes their nonce, identity, and signature over transcript. */ export interface SessionAccept { sessionId: string; responderDid: string; responderPublicKey: string; nonceB: string; acceptedAt: number; /** Base64 signature by responder over canonical(transcriptPayload). */ responderSignature: string; } /** Initiator's closing message — signs the *same* transcript as the responder. */ export interface SessionConfirm { sessionId: string; confirmedAt: number; /** Base64 signature by initiator over canonical(transcriptPayload). */ initiatorSignature: string; } /** A successfully verified mutual session — both sides proven. */ export interface SessionBindings { sessionId: string; initiatorDid: string; responderDid: string; nonceA: string; nonceB: string; purpose: string; initiatedAt: number; acceptedAt: number; confirmedAt: number; /** SHA-256 hex of canonical transcript, stable across both parties. */ transcriptHash: string; } export type SessionVerification = { valid: true; bindings: SessionBindings; } | { valid: false; reason: string; }; export declare function initiateSession(opts: { initiatorDid: string; initiatorPublicKey: string; purpose: string; ttlMs?: number | null; provider?: CryptoProvider; }): SessionInit; export declare function acceptSession(opts: { init: SessionInit; responderDid: string; responderPublicKey: string; responderSigningKey: Uint8Array; provider?: CryptoProvider; }): SessionAccept; export declare function confirmSession(opts: { init: SessionInit; accept: SessionAccept; initiatorSigningKey: Uint8Array; provider?: CryptoProvider; }): SessionConfirm; export declare function verifyMutualSession(opts: { init: SessionInit; accept: SessionAccept; confirm: SessionConfirm; /** Current time for freshness check (defaults to Date.now()). */ now?: number; /** Maximum age allowed for the handshake (ms). Optional. */ maxAgeMs?: number; provider?: CryptoProvider; /** * Optional DID method registry for non-did:key identities (did:web, did:pkh). * When absent, falls back to did:key binding semantics. */ registry?: DidMethodRegistry; }): SessionVerification; /** * Compute the canonical transcript hash for an (init, accept) pair. Both * parties should get the same value. Useful as a session identifier for * binding downstream operations. */ export declare function computeTranscriptHash(init: SessionInit, accept: SessionAccept, provider?: CryptoProvider): string; //# sourceMappingURL=mutual-session.d.ts.map