@cello-protocol/crypto 0.0.58 → 0.0.60

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.ts CHANGED
@@ -15,6 +15,10 @@ export { ed25519_FROST } from "@noble/curves/ed25519.js";
15
15
  export { buildCheckpointTbs, computeCheckpointHash } from "./checkpoint.js";
16
16
  export { buildRelayRegistrationTbs, verifyRelayRegistrationSignature } from "./relay-registration.js";
17
17
  export { sealToRecipient, openSealed, CONTENT_SEAL_OVERHEAD_BYTES } from "./content-seal.js";
18
+ export { generateSaltContribution, deriveSessionSalt, saltFingerprint, saltedContentHash, SALT_CONTRIBUTION_BYTES, SALT_FINGERPRINT_BYTES, SESSION_SALT_BYTES, } from "./session-salt.js";
19
+ export { generateSessionEphemeral, destroySessionEphemeral, deriveSessionSecrets, SESSION_KEY_BYTES, type SessionEphemeral, type SessionSecrets, } from "./session-key-agreement.js";
20
+ export { ephemeralSigningMessage, signSessionEphemeral, verifySessionEphemeral, EPHEMERAL_AUTH_REFUSALS, EPHEMERAL_SIG_BYTES, EPHEMERAL_PUBLIC_BYTES, type EphemeralAuthRefusal, type EphemeralAuthResult, } from "./session-ephemeral-auth.js";
21
+ export { sealSessionContent, openSessionContent, SESSION_CONTENT_SEAL_V1, SESSION_CONTENT_SEAL_OVERHEAD_BYTES, } from "./session-content-seal.js";
18
22
  export type { ManifestVerifyResult, ManifestVerifyDiagnostics, ManifestVerifySkipReason, ManifestVerifySkippedEntry, ConsortiumManifestInput } from "./manifest.js";
19
23
  export { canonicalManifestBody, verifyManifest } from "./manifest.js";
20
24
  export type { PreAuthCapability, PreAuthCapabilityBody, CapabilityVerifyResult, CapabilityVerifyReason } from "./preauth-capability.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACpI,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAClG,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,WAAW,EACX,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACxI,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3F,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,0BAA0B,EAC1B,YAAY,EACZ,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,6BAA6B,EAC7B,YAAY,EACZ,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAIzE,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,YAAY,GACb,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,iCAAiC,EACjC,wBAAwB,GACzB,MAAM,mCAAmC,CAAC;AAK3C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAGzD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG5E,OAAO,EAAE,yBAAyB,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAGtG,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAG7F,YAAY,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACpK,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEtE,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACxI,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACxI,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAG3F,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC9F,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -20,6 +20,21 @@ export { buildCheckpointTbs, computeCheckpointHash } from "./checkpoint.js";
20
20
  export { buildRelayRegistrationTbs, verifyRelayRegistrationSignature } from "./relay-registration.js";
21
21
  // M7-MSG-001: recipient content sealed box (E2E encryption for parked content)
22
22
  export { sealToRecipient, openSealed, CONTENT_SEAL_OVERHEAD_BYTES } from "./content-seal.js";
23
+ // Decisions Carried #8/#9/#10: the SESSION SALT — agreed in the same exchange as the envelope key
24
+ // but NOT derived from it, because the key must be destroyed at close and the salt must survive the
25
+ // session. Both sides contribute; HMAC for the salted content hash; a fingerprint for the open check.
26
+ export { generateSaltContribution, deriveSessionSalt, saltFingerprint, saltedContentHash, SALT_CONTRIBUTION_BYTES, SALT_FINGERPRINT_BYTES, SESSION_SALT_BYTES, } from "./session-salt.js";
27
+ // DOD-M15-KEYAGREE-1: CELLO's own per-session key agreement — ephemeral-ephemeral X25519 with the
28
+ // hybrid-PQ hook built in. ONE output, the envelope key: the salt is agreed separately above
29
+ // (Decisions Carried #8), because the key is destroyed at close and the salt outlives the session.
30
+ export { generateSessionEphemeral, destroySessionEphemeral, deriveSessionSecrets, SESSION_KEY_BYTES, } from "./session-key-agreement.js";
31
+ // DOD-M15-EPHEMERAL-AUTH-1: the throwaway key is SIGNED with the agent's identity and the peer's is
32
+ // verified BEFORE anything is derived from it — without this a relay substitutes both sides' keys
33
+ // and reads everything while both ends see a working conversation. Plus the AEAD that encrypts a
34
+ // message body under the agreed key: distinct from `content-seal.ts`, which seals PARKED content to
35
+ // a long-term identity key and deliberately has no forward secrecy.
36
+ export { ephemeralSigningMessage, signSessionEphemeral, verifySessionEphemeral, EPHEMERAL_AUTH_REFUSALS, EPHEMERAL_SIG_BYTES, EPHEMERAL_PUBLIC_BYTES, } from "./session-ephemeral-auth.js";
37
+ export { sealSessionContent, openSessionContent, SESSION_CONTENT_SEAL_V1, SESSION_CONTENT_SEAL_OVERHEAD_BYTES, } from "./session-content-seal.js";
23
38
  export { canonicalManifestBody, verifyManifest } from "./manifest.js";
24
39
  export { canonicalCapabilityBody, signCapability, verifyCapability, encodeCapability, decodeCapability } from "./preauth-capability.js";
25
40
  export { CONSORTIUM_ROOT_KEYS, CONSORTIUM_THRESHOLD, TEST_CONSORTIUM_ROOT_KEYS, TEST_CONSORTIUM_THRESHOLD, } from "./consortium-keys.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEpI,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,WAAW,EACX,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAExI,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAU3F,OAAO,EACL,6BAA6B,EAC7B,YAAY,EACZ,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAE1B,iGAAiG;AACjG,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,uGAAuG;AACvG,0FAA0F;AAC1F,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,YAAY,GACb,MAAM,4BAA4B,CAAC;AAEpC,8FAA8F;AAC9F,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,iCAAiC,EACjC,wBAAwB,GACzB,MAAM,mCAAmC,CAAC;AAE3C,qGAAqG;AACrG,mGAAmG;AACnG,qEAAqE;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,8EAA8E;AAC9E,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE5E,oEAAoE;AACpE,OAAO,EAAE,yBAAyB,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAEtG,+EAA+E;AAC/E,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAI7F,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGtE,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACxI,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAE3F,wEAAwE;AACxE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEpI,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,WAAW,EACX,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAExI,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAU3F,OAAO,EACL,6BAA6B,EAC7B,YAAY,EACZ,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAE1B,iGAAiG;AACjG,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,uGAAuG;AACvG,0FAA0F;AAC1F,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,YAAY,GACb,MAAM,4BAA4B,CAAC;AAEpC,8FAA8F;AAC9F,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,iCAAiC,EACjC,wBAAwB,GACzB,MAAM,mCAAmC,CAAC;AAE3C,qGAAqG;AACrG,mGAAmG;AACnG,qEAAqE;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,8EAA8E;AAC9E,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE5E,oEAAoE;AACpE,OAAO,EAAE,yBAAyB,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAEtG,+EAA+E;AAC/E,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAC7F,kGAAkG;AAClG,oGAAoG;AACpG,sGAAsG;AACtG,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,kGAAkG;AAClG,6FAA6F;AAC7F,mGAAmG;AACnG,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,GAGlB,MAAM,4BAA4B,CAAC;AAEpC,oGAAoG;AACpG,kGAAkG;AAClG,iGAAiG;AACjG,oGAAoG;AACpG,oEAAoE;AACpE,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GAGvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,mCAAmC,GACpC,MAAM,2BAA2B,CAAC;AAInC,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGtE,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACxI,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAE3F,wEAAwE;AACxE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-test-fixture.d.ts","sourceRoot":"","sources":["../src/manifest-test-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,CAM3F,CAAC;AAEX,sGAAsG;AACtG,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC/B,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,kBAAkB,EAAE,EAC3B,IAAI,CAAC,EAAE,oBAAoB,GAC1B,uBAAuB,CAoBzB;AAID;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B;;;CAG9B,CAAC"}
1
+ {"version":3,"file":"manifest-test-fixture.d.ts","sourceRoot":"","sources":["../src/manifest-test-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,CAM3F,CAAC;AAEX,sGAAsG;AACtG,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC/B,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,kBAAkB,EAAE,EAC3B,IAAI,CAAC,EAAE,oBAAoB,GAC1B,uBAAuB,CA+BzB;AAID;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B;;;CAG9B,CAAC"}
@@ -34,7 +34,18 @@ export function makeTestManifest(nodes, opts) {
34
34
  const manifest = {
35
35
  version: opts?.version ?? 1,
36
36
  not_before: opts?.notBefore ?? "2026-01-01T00:00:00Z",
37
- expires: opts?.expires ?? "2027-01-01T00:00:00Z",
37
+ /**
38
+ * ⚠️ A FIXTURE THAT EXPIRES IS A TEST SUITE WITH A FUSE. This defaulted to `2027-01-01`, four
39
+ * months out when it was found (2026-08-24) — after which every default-fixture manifest becomes
40
+ * EXPIRED and every test that needs an in-window one goes red **on a date, with no code change**.
41
+ * It would also have silently switched on the lapsed-manifest branches added by
42
+ * `DOD-M15-EXPIRY-CONSUMER-POLICY-1` for every existing verifier test at the same moment.
43
+ *
44
+ * Far-future and FIXED, not `Date.now() + 1y`: a rolling default makes the fixture's window
45
+ * depend on the clock, and the tests that care about window boundaries pass their own dates
46
+ * anyway — which is the reason this default only ever needs to mean "in window".
47
+ */
48
+ expires: opts?.expires ?? "2099-01-01T00:00:00Z",
38
49
  nodes: nodes,
39
50
  signatures: [],
40
51
  };
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-test-fixture.js","sourceRoot":"","sources":["../src/manifest-test-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA0E;IACvG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC7B,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC7B,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC7B,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC7B,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;CACrB,CAAC;AAqBX;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAA2B,EAC3B,IAA2B;IAE3B,MAAM,QAAQ,GAA4B;QACxC,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,CAAC;QAC3B,UAAU,EAAE,IAAI,EAAE,SAAS,IAAI,sBAAsB;QACrD,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,sBAAsB;QAChD,KAAK,EAAE,KAA2C;QAClD,UAAU,EAAE,EAAE;KACf,CAAC;IAEF,qCAAqC;IACrC,MAAM,IAAI,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAE7C,sDAAsD;IACtD,MAAM,UAAU,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACzC,YAAY,EAAE,GAAG;QACjB,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;KACpF,CAAC,CAAC,CAAC;IAEJ,QAAQ,CAAC,UAAU,GAAG,UAAU,CAAC;IACjC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,gFAAgF;AAEhF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,aAAa,EAAE,kEAAkE;IACjF,YAAY,EAAE,kEAAkE;CACxE,CAAC"}
1
+ {"version":3,"file":"manifest-test-fixture.js","sourceRoot":"","sources":["../src/manifest-test-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA0E;IACvG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC7B,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC7B,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC7B,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC7B,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;CACrB,CAAC;AAqBX;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAA2B,EAC3B,IAA2B;IAE3B,MAAM,QAAQ,GAA4B;QACxC,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,CAAC;QAC3B,UAAU,EAAE,IAAI,EAAE,SAAS,IAAI,sBAAsB;QACrD;;;;;;;;;;WAUG;QACH,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,sBAAsB;QAChD,KAAK,EAAE,KAA2C;QAClD,UAAU,EAAE,EAAE;KACf,CAAC;IAEF,qCAAqC;IACrC,MAAM,IAAI,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAE7C,sDAAsD;IACtD,MAAM,UAAU,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACzC,YAAY,EAAE,GAAG;QACjB,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;KACpF,CAAC,CAAC,CAAC;IAEJ,QAAQ,CAAC,UAAU,GAAG,UAAU,CAAC;IACjC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,gFAAgF;AAEhF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,aAAa,EAAE,kEAAkE;IACjF,YAAY,EAAE,kEAAkE;CACxE,CAAC"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * DOD-M15-EPHEMERAL-AUTH-1 — encrypting a message body under the AGREED SESSION KEY.
3
+ *
4
+ * ─── What this is for, and how it differs from `content-seal.ts` ───────────────────────────────
5
+ *
6
+ * Two encryption schemes exist in this codebase and they are NOT interchangeable. Which one applies
7
+ * is decided by the ROUTE a message takes, and getting that wrong is silent:
8
+ *
9
+ * **This file — LIVE messages.** Encrypted under the per-session key both sides agreed from
10
+ * throwaway keypairs (`session-key-agreement.ts`). Those keypairs are destroyed when the session
11
+ * ends, so a recording of the traffic cannot be decrypted later even if both agents' long-term
12
+ * identity keys are eventually compromised. That is forward secrecy, and it is the whole point.
13
+ *
14
+ * **`content-seal.ts` — PARKED messages.** Encrypted to the recipient's LONG-TERM identity key,
15
+ * because the recipient is offline and cannot take part in a handshake. It has no forward secrecy
16
+ * by construction: whoever holds that identity key can open every message ever sealed to it. That
17
+ * is an accepted trade for the mailbox and must not be copied here.
18
+ *
19
+ * ⚠️ A message that fails live delivery falls back to the mailbox, so the SAME message can take
20
+ * either scheme. The caller must encrypt the copy that goes on the wire and leave the plaintext it
21
+ * holds for the park backstop alone — sealing an already-encrypted body to the identity key gives
22
+ * the recipient bytes locked under a session key that is about to be destroyed.
23
+ *
24
+ * ─── Construction (SPARC Phase P) ──────────────────────────────────────────────────────────────
25
+ *
26
+ * sealSessionContent(key, plaintext):
27
+ * 1. iv = random 12 bytes # NIST SP 800-38D §8.2.2
28
+ * 2. ct||tag = AES-256-GCM(key, iv, plaintext) # NIST SP 800-38D
29
+ * 3. blob = VERSION(1) || iv(12) || ct || tag(16)
30
+ *
31
+ * openSessionContent(key, blob) -> plaintext | null
32
+ *
33
+ * AES-256-GCM matches `content-seal.ts` rather than introducing a second AEAD, so there is one
34
+ * primitive to reason about. RFCs: HKDF — RFC 5869 (upstream, in the key agreement); AES-GCM — NIST
35
+ * SP 800-38D.
36
+ *
37
+ * ─── Why a RANDOM IV rather than a counter ─────────────────────────────────────────────────────
38
+ *
39
+ * A counter is the stronger construction and it needs state that survives a restart. This session
40
+ * key does not: the secret is deliberately never persisted, and a revived session RE-KEYS
41
+ * (Decisions Carried #5), so a counter would restart at zero under a NEW key — which is safe, and
42
+ * would also have to be proven safe at every future change to the revival rule. A 96-bit random IV
43
+ * under a key that lives for one session, with a birthday bound around 2^32 messages, is not the
44
+ * limiting factor here and needs no state at all.
45
+ *
46
+ * ─── The VERSION byte is FRAMING, not compatibility ────────────────────────────────────────────
47
+ *
48
+ * There are no other builds to be compatible with — CELLO is pre-launch with no external installs —
49
+ * so this byte is not here to interoperate with anything. It is here because the alternative is a
50
+ * blob whose layout is implied by its length, and a reader that meets bytes it cannot frame would
51
+ * then hand attacker-chosen input to a decryptor under a guessed layout. One byte, one comparison,
52
+ * and an unknown value REFUSES rather than parsing.
53
+ *
54
+ * That is the only justification, and it is worth stating because the tempting one — "so a future
55
+ * version can change the format" — would be exactly the speculative scaffolding this codebase has
56
+ * ruled against building.
57
+ */
58
+ /**
59
+ * The only version this build writes, and the only one it reads.
60
+ *
61
+ * `0x01` rather than `0x00`: a zero first byte is what an all-zero buffer and a truncated read both
62
+ * look like, so a version of zero cannot be told from an absent one.
63
+ */
64
+ export declare const SESSION_CONTENT_SEAL_V1 = 1;
65
+ /** Bytes added to every message: version + IV + tag. */
66
+ export declare const SESSION_CONTENT_SEAL_OVERHEAD_BYTES: number;
67
+ /**
68
+ * Encrypt a message body under the agreed session key.
69
+ *
70
+ * Throws on a wrong-width key rather than deriving from whatever it was given: a short key silently
71
+ * accepted is encryption that appears to work under something that is not the agreed secret.
72
+ */
73
+ export declare function sealSessionContent(key: Uint8Array, plaintext: Uint8Array): Uint8Array;
74
+ /**
75
+ * Decrypt a message body under the agreed session key.
76
+ *
77
+ * **Returns `null` on every failure and never throws** — wrong key, tampered ciphertext, truncated
78
+ * blob, unknown version. The caller cannot tell those apart and must not try: GCM's tag check is the
79
+ * only thing that distinguishes "not for us" from "modified in flight", and a caller that branched
80
+ * on the difference would be branching on attacker-controlled input.
81
+ *
82
+ * The caller's job on `null` is to refuse the message loudly, not to retry or to fall back to
83
+ * reading it as plaintext.
84
+ */
85
+ export declare function openSessionContent(key: Uint8Array, blob: Uint8Array): Uint8Array | null;
86
+ //# sourceMappingURL=session-content-seal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-content-seal.d.ts","sourceRoot":"","sources":["../src/session-content-seal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAWH;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,IAAO,CAAC;AAE5C,wDAAwD;AACxD,eAAO,MAAM,mCAAmC,QAA2B,CAAC;AAE5E;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,GAAG,UAAU,CAerF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAiBvF"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * DOD-M15-EPHEMERAL-AUTH-1 — encrypting a message body under the AGREED SESSION KEY.
3
+ *
4
+ * ─── What this is for, and how it differs from `content-seal.ts` ───────────────────────────────
5
+ *
6
+ * Two encryption schemes exist in this codebase and they are NOT interchangeable. Which one applies
7
+ * is decided by the ROUTE a message takes, and getting that wrong is silent:
8
+ *
9
+ * **This file — LIVE messages.** Encrypted under the per-session key both sides agreed from
10
+ * throwaway keypairs (`session-key-agreement.ts`). Those keypairs are destroyed when the session
11
+ * ends, so a recording of the traffic cannot be decrypted later even if both agents' long-term
12
+ * identity keys are eventually compromised. That is forward secrecy, and it is the whole point.
13
+ *
14
+ * **`content-seal.ts` — PARKED messages.** Encrypted to the recipient's LONG-TERM identity key,
15
+ * because the recipient is offline and cannot take part in a handshake. It has no forward secrecy
16
+ * by construction: whoever holds that identity key can open every message ever sealed to it. That
17
+ * is an accepted trade for the mailbox and must not be copied here.
18
+ *
19
+ * ⚠️ A message that fails live delivery falls back to the mailbox, so the SAME message can take
20
+ * either scheme. The caller must encrypt the copy that goes on the wire and leave the plaintext it
21
+ * holds for the park backstop alone — sealing an already-encrypted body to the identity key gives
22
+ * the recipient bytes locked under a session key that is about to be destroyed.
23
+ *
24
+ * ─── Construction (SPARC Phase P) ──────────────────────────────────────────────────────────────
25
+ *
26
+ * sealSessionContent(key, plaintext):
27
+ * 1. iv = random 12 bytes # NIST SP 800-38D §8.2.2
28
+ * 2. ct||tag = AES-256-GCM(key, iv, plaintext) # NIST SP 800-38D
29
+ * 3. blob = VERSION(1) || iv(12) || ct || tag(16)
30
+ *
31
+ * openSessionContent(key, blob) -> plaintext | null
32
+ *
33
+ * AES-256-GCM matches `content-seal.ts` rather than introducing a second AEAD, so there is one
34
+ * primitive to reason about. RFCs: HKDF — RFC 5869 (upstream, in the key agreement); AES-GCM — NIST
35
+ * SP 800-38D.
36
+ *
37
+ * ─── Why a RANDOM IV rather than a counter ─────────────────────────────────────────────────────
38
+ *
39
+ * A counter is the stronger construction and it needs state that survives a restart. This session
40
+ * key does not: the secret is deliberately never persisted, and a revived session RE-KEYS
41
+ * (Decisions Carried #5), so a counter would restart at zero under a NEW key — which is safe, and
42
+ * would also have to be proven safe at every future change to the revival rule. A 96-bit random IV
43
+ * under a key that lives for one session, with a birthday bound around 2^32 messages, is not the
44
+ * limiting factor here and needs no state at all.
45
+ *
46
+ * ─── The VERSION byte is FRAMING, not compatibility ────────────────────────────────────────────
47
+ *
48
+ * There are no other builds to be compatible with — CELLO is pre-launch with no external installs —
49
+ * so this byte is not here to interoperate with anything. It is here because the alternative is a
50
+ * blob whose layout is implied by its length, and a reader that meets bytes it cannot frame would
51
+ * then hand attacker-chosen input to a decryptor under a guessed layout. One byte, one comparison,
52
+ * and an unknown value REFUSES rather than parsing.
53
+ *
54
+ * That is the only justification, and it is worth stating because the tempting one — "so a future
55
+ * version can change the format" — would be exactly the speculative scaffolding this codebase has
56
+ * ruled against building.
57
+ */
58
+ import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
59
+ /** AES-256. */
60
+ const KEY_BYTES = 32;
61
+ /** GCM's standard nonce length (NIST SP 800-38D §8.2). */
62
+ const IV_BYTES = 12;
63
+ /** GCM tag. Full length, never truncated. */
64
+ const TAG_BYTES = 16;
65
+ /**
66
+ * The only version this build writes, and the only one it reads.
67
+ *
68
+ * `0x01` rather than `0x00`: a zero first byte is what an all-zero buffer and a truncated read both
69
+ * look like, so a version of zero cannot be told from an absent one.
70
+ */
71
+ export const SESSION_CONTENT_SEAL_V1 = 0x01;
72
+ /** Bytes added to every message: version + IV + tag. */
73
+ export const SESSION_CONTENT_SEAL_OVERHEAD_BYTES = 1 + IV_BYTES + TAG_BYTES;
74
+ /**
75
+ * Encrypt a message body under the agreed session key.
76
+ *
77
+ * Throws on a wrong-width key rather than deriving from whatever it was given: a short key silently
78
+ * accepted is encryption that appears to work under something that is not the agreed secret.
79
+ */
80
+ export function sealSessionContent(key, plaintext) {
81
+ if (key.length !== KEY_BYTES) {
82
+ throw new Error(`SESSION CONTENT SEAL: the session key must be ${KEY_BYTES} bytes, got ${key.length}. This is a ` +
83
+ "LOCAL defect — the key comes from this side's own agreement, not from the peer. Refusing " +
84
+ "rather than encrypting under something that is not the agreed secret.");
85
+ }
86
+ const iv = new Uint8Array(randomBytes(IV_BYTES));
87
+ const cipher = createCipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
88
+ const ct = Buffer.concat([cipher.update(Buffer.from(plaintext)), cipher.final()]);
89
+ const tag = cipher.getAuthTag();
90
+ return new Uint8Array(Buffer.concat([Buffer.from([SESSION_CONTENT_SEAL_V1]), Buffer.from(iv), ct, tag]));
91
+ }
92
+ /**
93
+ * Decrypt a message body under the agreed session key.
94
+ *
95
+ * **Returns `null` on every failure and never throws** — wrong key, tampered ciphertext, truncated
96
+ * blob, unknown version. The caller cannot tell those apart and must not try: GCM's tag check is the
97
+ * only thing that distinguishes "not for us" from "modified in flight", and a caller that branched
98
+ * on the difference would be branching on attacker-controlled input.
99
+ *
100
+ * The caller's job on `null` is to refuse the message loudly, not to retry or to fall back to
101
+ * reading it as plaintext.
102
+ */
103
+ export function openSessionContent(key, blob) {
104
+ if (key.length !== KEY_BYTES)
105
+ return null;
106
+ // A blob must have room for version + IV + tag, and one byte cannot be assumed present: an empty
107
+ // frame would otherwise index past the end and read `undefined` as a version.
108
+ if (blob.length < SESSION_CONTENT_SEAL_OVERHEAD_BYTES)
109
+ return null;
110
+ if (blob[0] !== SESSION_CONTENT_SEAL_V1)
111
+ return null;
112
+ const iv = blob.subarray(1, 1 + IV_BYTES);
113
+ const ct = blob.subarray(1 + IV_BYTES, blob.length - TAG_BYTES);
114
+ const tag = blob.subarray(blob.length - TAG_BYTES);
115
+ try {
116
+ const decipher = createDecipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
117
+ decipher.setAuthTag(Buffer.from(tag));
118
+ return new Uint8Array(Buffer.concat([decipher.update(Buffer.from(ct)), decipher.final()]));
119
+ }
120
+ catch {
121
+ return null;
122
+ }
123
+ }
124
+ //# sourceMappingURL=session-content-seal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-content-seal.js","sourceRoot":"","sources":["../src/session-content-seal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * DOD-M15-EPHEMERAL-AUTH-1 — binding the throwaway key to the agent's identity.
3
+ *
4
+ * ─── The attack this closes, step by step ──────────────────────────────────────────────────────
5
+ *
6
+ * The key agreement mixes two throwaway keys into a shared secret, and the secret itself never
7
+ * crosses the wire. But if nobody SIGNS the throwaway key, an arriving one carries no evidence of
8
+ * who sent it:
9
+ *
10
+ * 1. Alice sends her throwaway public key. The relay is carrying the traffic.
11
+ * 2. The relay keeps Alice's and forwards its OWN key to Bob instead.
12
+ * 3. It does the same in the other direction.
13
+ * 4. It now shares one secret with Alice and a different one with Bob.
14
+ * 5. It decrypts everything, reads it, re-encrypts it, and passes it on. Neither side sees
15
+ * anything wrong, because both halves of the conversation decrypt perfectly.
16
+ *
17
+ * So the unauthenticated layer stops a PASSIVE recorder — which is what harvest-now-decrypt-later
18
+ * is about — and does nothing against the party actually carrying the messages. **And we run the
19
+ * relays**, so without this the guarantee reduces to "trust us", which is the one thing CELLO exists
20
+ * so nobody has to do.
21
+ *
22
+ * ─── What is signed, and why the session id is in it ───────────────────────────────────────────
23
+ *
24
+ * message = "cello/session/v1/ephemeral" || sessionId || ephemeralPublic
25
+ *
26
+ * The LABEL makes this signature unusable as any other kind of CELLO signature, and vice versa: an
27
+ * agent signs many things, and a signature that is valid in two contexts is a signature an attacker
28
+ * can move between them.
29
+ *
30
+ * The SESSION ID binds the ephemeral to the conversation it was minted for. Without it, a signed
31
+ * ephemeral captured from one session could be replayed into another between the same two agents —
32
+ * the signature verifies, both sides derive, and the relay that replayed it knows the secret from
33
+ * the session it harvested. The session id is length-prefixed so that `sessionId ‖ ephemeral` cannot
34
+ * be re-split at a different point to produce the same bytes from different inputs.
35
+ *
36
+ * ⚠️ WHAT THIS DOES NOT DO. It proves the ephemeral came from the holder of that identity key. It
37
+ * does not prove the identity key is the agent you meant to talk to — that is the counterparty
38
+ * identity the caller passes in, and the caller must take it from what the OPERATOR asked for, never
39
+ * from a value the directory or the relay handed back. Verifying against a key the attacker chose is
40
+ * relocating the trust rather than closing it.
41
+ */
42
+ /** Ed25519 signatures are 64 bytes; X25519 publics are 32. */
43
+ export declare const EPHEMERAL_SIG_BYTES = 64;
44
+ export declare const EPHEMERAL_PUBLIC_BYTES = 32;
45
+ /**
46
+ * The exact bytes both sides sign and verify.
47
+ *
48
+ * Written once and used by BOTH directions on purpose. A signer and a verifier that each build the
49
+ * message from their own reading of a spec is how two implementations end up disagreeing about a
50
+ * byte and rejecting each other for a reason neither can see.
51
+ */
52
+ export declare function ephemeralSigningMessage(sessionId: Uint8Array, ephemeralPublic: Uint8Array): Uint8Array;
53
+ /** Sign this side's throwaway public key with the agent's long-term identity key. */
54
+ export declare function signSessionEphemeral(signer: {
55
+ sign(data: Uint8Array): Promise<Uint8Array>;
56
+ }, sessionId: Uint8Array, ephemeralPublic: Uint8Array): Promise<Uint8Array>;
57
+ /**
58
+ * Why a peer's ephemeral was refused. A CLOSED set — a free-form string is what lets a new code
59
+ * reach a caller with nothing to act on (`refusal-reasons.ts` records what that cost).
60
+ */
61
+ export declare const EPHEMERAL_AUTH_REFUSALS: {
62
+ /** No signature at all. */
63
+ readonly SIGNATURE_MISSING: "ephemeral_signature_missing";
64
+ /** Present but the wrong width, or the public key is the wrong width. */
65
+ readonly MALFORMED: "ephemeral_malformed";
66
+ /** Verified against the expected identity and did not match. */
67
+ readonly SIGNATURE_MISMATCH: "ephemeral_signature_mismatch";
68
+ };
69
+ export type EphemeralAuthRefusal = (typeof EPHEMERAL_AUTH_REFUSALS)[keyof typeof EPHEMERAL_AUTH_REFUSALS];
70
+ export type EphemeralAuthResult = {
71
+ ok: true;
72
+ } | {
73
+ ok: false;
74
+ reason: EphemeralAuthRefusal;
75
+ detail: string;
76
+ };
77
+ /**
78
+ * VERIFY A PEER'S EPHEMERAL BEFORE ANYTHING IS DERIVED FROM IT.
79
+ *
80
+ * 🚨 MISSING, MALFORMED AND MISMATCHED ALL FAIL. They are separate REASONS because they send an
81
+ * operator to different places, and they are the same OUTCOME because an attacker evading a mismatch
82
+ * check simply supplies no signature at all. "We could not tell" must never be more forgiving than
83
+ * "we proved it wrong" — that is the hole, not the mismatch.
84
+ *
85
+ * Called BEFORE `deriveSessionSecrets`, never alongside it. Deriving first and checking after means
86
+ * a key exists, briefly, that was agreed with someone unproven — and every "briefly" in this
87
+ * codebase eventually turns out to be long enough for something to read it.
88
+ */
89
+ export declare function verifySessionEphemeral(opts: {
90
+ /** The identity we EXPECT — from what the operator asked for, never from the directory or relay. */
91
+ expectedIdentityPublic: Uint8Array;
92
+ sessionId: Uint8Array;
93
+ peerEphemeralPublic: Uint8Array | undefined;
94
+ peerSignature: Uint8Array | undefined;
95
+ }): EphemeralAuthResult;
96
+ //# sourceMappingURL=session-ephemeral-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-ephemeral-auth.d.ts","sourceRoot":"","sources":["../src/session-ephemeral-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AASH,8DAA8D;AAC9D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,GAAG,UAAU,CActG;AAED,qFAAqF;AACrF,wBAAsB,oBAAoB,CACxC,MAAM,EAAE;IAAE,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;CAAE,EACvD,SAAS,EAAE,UAAU,EACrB,eAAe,EAAE,UAAU,GAC1B,OAAO,CAAC,UAAU,CAAC,CAErB;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB;IAClC,2BAA2B;;IAE3B,yEAAyE;;IAEzE,gEAAgE;;CAExD,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAC9B,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,OAAO,uBAAuB,CAAC,CAAC;AAEzE,MAAM,MAAM,mBAAmB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,oGAAoG;IACpG,sBAAsB,EAAE,UAAU,CAAC;IACnC,SAAS,EAAE,UAAU,CAAC;IACtB,mBAAmB,EAAE,UAAU,GAAG,SAAS,CAAC;IAC5C,aAAa,EAAE,UAAU,GAAG,SAAS,CAAC;CACvC,GAAG,mBAAmB,CAwCtB"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * DOD-M15-EPHEMERAL-AUTH-1 — binding the throwaway key to the agent's identity.
3
+ *
4
+ * ─── The attack this closes, step by step ──────────────────────────────────────────────────────
5
+ *
6
+ * The key agreement mixes two throwaway keys into a shared secret, and the secret itself never
7
+ * crosses the wire. But if nobody SIGNS the throwaway key, an arriving one carries no evidence of
8
+ * who sent it:
9
+ *
10
+ * 1. Alice sends her throwaway public key. The relay is carrying the traffic.
11
+ * 2. The relay keeps Alice's and forwards its OWN key to Bob instead.
12
+ * 3. It does the same in the other direction.
13
+ * 4. It now shares one secret with Alice and a different one with Bob.
14
+ * 5. It decrypts everything, reads it, re-encrypts it, and passes it on. Neither side sees
15
+ * anything wrong, because both halves of the conversation decrypt perfectly.
16
+ *
17
+ * So the unauthenticated layer stops a PASSIVE recorder — which is what harvest-now-decrypt-later
18
+ * is about — and does nothing against the party actually carrying the messages. **And we run the
19
+ * relays**, so without this the guarantee reduces to "trust us", which is the one thing CELLO exists
20
+ * so nobody has to do.
21
+ *
22
+ * ─── What is signed, and why the session id is in it ───────────────────────────────────────────
23
+ *
24
+ * message = "cello/session/v1/ephemeral" || sessionId || ephemeralPublic
25
+ *
26
+ * The LABEL makes this signature unusable as any other kind of CELLO signature, and vice versa: an
27
+ * agent signs many things, and a signature that is valid in two contexts is a signature an attacker
28
+ * can move between them.
29
+ *
30
+ * The SESSION ID binds the ephemeral to the conversation it was minted for. Without it, a signed
31
+ * ephemeral captured from one session could be replayed into another between the same two agents —
32
+ * the signature verifies, both sides derive, and the relay that replayed it knows the secret from
33
+ * the session it harvested. The session id is length-prefixed so that `sessionId ‖ ephemeral` cannot
34
+ * be re-split at a different point to produce the same bytes from different inputs.
35
+ *
36
+ * ⚠️ WHAT THIS DOES NOT DO. It proves the ephemeral came from the holder of that identity key. It
37
+ * does not prove the identity key is the agent you meant to talk to — that is the counterparty
38
+ * identity the caller passes in, and the caller must take it from what the OPERATOR asked for, never
39
+ * from a value the directory or the relay handed back. Verifying against a key the attacker chose is
40
+ * relocating the trust rather than closing it.
41
+ */
42
+ import { verify as edVerify } from "./ed25519.js";
43
+ const ENC = new TextEncoder();
44
+ /** Domain separation. Versioned, so a future change is a new label rather than a reinterpretation. */
45
+ const EPHEMERAL_SIG_LABEL = ENC.encode("cello/session/v1/ephemeral");
46
+ /** Ed25519 signatures are 64 bytes; X25519 publics are 32. */
47
+ export const EPHEMERAL_SIG_BYTES = 64;
48
+ export const EPHEMERAL_PUBLIC_BYTES = 32;
49
+ /**
50
+ * The exact bytes both sides sign and verify.
51
+ *
52
+ * Written once and used by BOTH directions on purpose. A signer and a verifier that each build the
53
+ * message from their own reading of a spec is how two implementations end up disagreeing about a
54
+ * byte and rejecting each other for a reason neither can see.
55
+ */
56
+ export function ephemeralSigningMessage(sessionId, ephemeralPublic) {
57
+ // The session id is LENGTH-PREFIXED (4 bytes, big-endian). Concatenating two variable-length
58
+ // fields lets a crafted pair re-split at a different boundary and produce identical bytes from
59
+ // different inputs — the signature would then cover something other than what it appears to.
60
+ const out = new Uint8Array(EPHEMERAL_SIG_LABEL.length + 4 + sessionId.length + ephemeralPublic.length);
61
+ let o = 0;
62
+ out.set(EPHEMERAL_SIG_LABEL, o);
63
+ o += EPHEMERAL_SIG_LABEL.length;
64
+ out[o++] = (sessionId.length >>> 24) & 0xff;
65
+ out[o++] = (sessionId.length >>> 16) & 0xff;
66
+ out[o++] = (sessionId.length >>> 8) & 0xff;
67
+ out[o++] = sessionId.length & 0xff;
68
+ out.set(sessionId, o);
69
+ o += sessionId.length;
70
+ out.set(ephemeralPublic, o);
71
+ return out;
72
+ }
73
+ /** Sign this side's throwaway public key with the agent's long-term identity key. */
74
+ export async function signSessionEphemeral(signer, sessionId, ephemeralPublic) {
75
+ return signer.sign(ephemeralSigningMessage(sessionId, ephemeralPublic));
76
+ }
77
+ /**
78
+ * Why a peer's ephemeral was refused. A CLOSED set — a free-form string is what lets a new code
79
+ * reach a caller with nothing to act on (`refusal-reasons.ts` records what that cost).
80
+ */
81
+ export const EPHEMERAL_AUTH_REFUSALS = {
82
+ /** No signature at all. */
83
+ SIGNATURE_MISSING: "ephemeral_signature_missing",
84
+ /** Present but the wrong width, or the public key is the wrong width. */
85
+ MALFORMED: "ephemeral_malformed",
86
+ /** Verified against the expected identity and did not match. */
87
+ SIGNATURE_MISMATCH: "ephemeral_signature_mismatch",
88
+ };
89
+ /**
90
+ * VERIFY A PEER'S EPHEMERAL BEFORE ANYTHING IS DERIVED FROM IT.
91
+ *
92
+ * 🚨 MISSING, MALFORMED AND MISMATCHED ALL FAIL. They are separate REASONS because they send an
93
+ * operator to different places, and they are the same OUTCOME because an attacker evading a mismatch
94
+ * check simply supplies no signature at all. "We could not tell" must never be more forgiving than
95
+ * "we proved it wrong" — that is the hole, not the mismatch.
96
+ *
97
+ * Called BEFORE `deriveSessionSecrets`, never alongside it. Deriving first and checking after means
98
+ * a key exists, briefly, that was agreed with someone unproven — and every "briefly" in this
99
+ * codebase eventually turns out to be long enough for something to read it.
100
+ */
101
+ export function verifySessionEphemeral(opts) {
102
+ if (opts.peerSignature === undefined) {
103
+ return {
104
+ ok: false,
105
+ reason: EPHEMERAL_AUTH_REFUSALS.SIGNATURE_MISSING,
106
+ detail: "the peer sent a session key with no signature over it, so there is nothing to tie it to " +
107
+ "your counterparty. An unsigned key is exactly what a relay substituting its own would send, " +
108
+ "and it is refused on the same path as a wrong signature — a check that is lenient about a " +
109
+ "missing proof is a check an attacker skips.",
110
+ };
111
+ }
112
+ if (opts.peerSignature.length !== EPHEMERAL_SIG_BYTES ||
113
+ opts.peerEphemeralPublic === undefined ||
114
+ opts.peerEphemeralPublic.length !== EPHEMERAL_PUBLIC_BYTES) {
115
+ return {
116
+ ok: false,
117
+ reason: EPHEMERAL_AUTH_REFUSALS.MALFORMED,
118
+ detail: `the peer's session key material is the wrong shape — signature ${opts.peerSignature.length} ` +
119
+ `bytes (expected ${EPHEMERAL_SIG_BYTES}), key ${opts.peerEphemeralPublic?.length ?? 0} bytes ` +
120
+ `(expected ${EPHEMERAL_PUBLIC_BYTES}). Refusing rather than padding: a short value silently ` +
121
+ "zero-extended is an agreement with something that is not the peer.",
122
+ };
123
+ }
124
+ const message = ephemeralSigningMessage(opts.sessionId, opts.peerEphemeralPublic);
125
+ if (!edVerify(opts.expectedIdentityPublic, message, opts.peerSignature)) {
126
+ return {
127
+ ok: false,
128
+ reason: EPHEMERAL_AUTH_REFUSALS.SIGNATURE_MISMATCH,
129
+ detail: "the peer's session key is signed, but not by the counterparty this session is with. That " +
130
+ "is what a relay substituting its own key looks like: it would then share one secret with " +
131
+ "you and another with them, and read everything in between while both sides saw a working " +
132
+ "conversation. Refusing.",
133
+ };
134
+ }
135
+ return { ok: true };
136
+ }
137
+ //# sourceMappingURL=session-ephemeral-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-ephemeral-auth.js","sourceRoot":"","sources":["../src/session-ephemeral-auth.ts"],"names":[],"mappings":"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"}