@metalabel/dfos-protocol 0.51.0 → 0.52.1

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.
@@ -1,5 +1,5 @@
1
- import { I as IdentityOperation, v as Signer, x as VerifiedIdentity, S as ServiceEntry, c as ContentOperation, R as ResolvedIdentity, r as RevocationChecker, d as CountersignPayload, a as ArtifactPayload } from '../dfos-credential-CtG0_n4P.js';
2
- export { A as ARTIFACT_CID_ANCHOR_RE, C as CONTENT_ID_ANCHOR_RE, f as CreditClaimPayload, g as DeclaredKeyState, h as Iso8601, M as MAX_ARTIFACT_PAYLOAD_SIZE, j as MAX_CREDIT_CLAIM_SIZE, k as MAX_KEY_PROOFS, l as MAX_OPERATION_SIZE, m as MAX_SERVICES_ENTRIES, n as MAX_SERVICES_PAYLOAD_SIZE, o as MAX_SIGN_REQUEST_PAYLOAD_SIZE, p as MAX_SIGN_REQUEST_SIZE, q as MultikeyPublicKey, s as RevocationPayload, t as ServicesArray, u as SignRequestPayload, y as VoidKeyMembership, G as parseProtocolTimestampUnix } from '../dfos-credential-CtG0_n4P.js';
1
+ import { I as IdentityOperation, v as Signer, x as VerifiedIdentity, S as ServiceEntry, c as ContentOperation, R as ResolvedIdentity, r as RevocationChecker, d as CountersignPayload, a as ArtifactPayload } from '../dfos-credential-j-kHtFvG.js';
2
+ export { A as ARTIFACT_CID_ANCHOR_RE, C as CONTENT_ID_ANCHOR_RE, f as CreditClaimPayload, g as DeclaredKeyState, h as Iso8601, M as MAX_ARTIFACT_PAYLOAD_SIZE, j as MAX_CREDIT_CLAIM_SIZE, k as MAX_KEY_PROOFS, l as MAX_OPERATION_SIZE, m as MAX_SERVICES_ENTRIES, n as MAX_SERVICES_PAYLOAD_SIZE, o as MAX_SIGN_REQUEST_PAYLOAD_SIZE, p as MAX_SIGN_REQUEST_SIZE, q as MultikeyPublicKey, s as RevocationPayload, t as ServicesArray, u as SignRequestPayload, y as VoidKeyMembership, G as parseProtocolTimestampUnix } from '../dfos-credential-j-kHtFvG.js';
3
3
  import 'zod';
4
4
 
5
5
  /** Ed25519 public key multicodec value */
@@ -22,6 +22,17 @@ declare const decodeMultikey: (multibase: string) => {
22
22
  keyBytes: Uint8Array;
23
23
  codec: number;
24
24
  };
25
+ /**
26
+ * Decode a Multikey multibase string that MUST carry an Ed25519 PUBLIC key
27
+ *
28
+ * `decodeMultikey` also accepts the `ed25519-priv` prefix and reports it in
29
+ * `codec`, which a caller that destructures only `{ keyBytes }` silently
30
+ * discards — so a private-key-tagged multibase string would verify as if it were
31
+ * a public key, where the Go reference (DecodeMultikey) accepts the 0xed01
32
+ * prefix and nothing else. This is the decode every signature path takes, so the
33
+ * codec assertion cannot be forgotten at the next call site.
34
+ */
35
+ declare const decodeEd25519PublicMultikey: (multibase: string) => Uint8Array;
25
36
 
26
37
  /**
27
38
  * Derive a prefixed chain identifier from CID bytes
@@ -529,7 +540,8 @@ declare const buildSignRequest: (input: {
529
540
  requestCID: string;
530
541
  }>;
531
542
  /**
532
- * Verify a sign-request envelope in SIGNING.md's exact 1–9 order.
543
+ * Verify a sign-request envelope in RELAY.md, Envelope verification's exact 1–9
544
+ * order.
533
545
  *
534
546
  * `resolveIdentity` supplies CURRENT identity state. A missing identity or
535
547
  * resolver failure is `unverifiable`; a deleted identity, missing current key,
@@ -548,4 +560,4 @@ declare const assertCanonicalSignRequestPayload: (payloadTyp: string, payloadByt
548
560
  subject: string;
549
561
  }) => void;
550
562
 
551
- export { type AnchorKind, ArtifactPayload, ContentOperation, CountersignPayload, type CreditClaimFailureReason, CreditClaimVerifyError, type CreditEntry, type CreditEntryState, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, RECOGNIZED_SERVICE_TYPES, ServiceEntry, type SignRequestFailureReason, SignRequestVerifyError, Signer, type VerifiedArtifact, type VerifiedContentChain, type VerifiedCountersignature, type VerifiedCreditClaim, type VerifiedCreditEntry, VerifiedIdentity, type VerifiedRevocation, type VerifiedSignRequest, anchorsByLabel, assertCanonicalSignRequestPayload, assertServicesWithinCap, buildSignRequest, classifyAnchor, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, isRecognizedServiceType, relayEndpoints, signArtifact, signContentOperation, signCountersignature, signCreditClaim, signIdentityOperation, signRevocation, verifyArtifact, verifyContentChain, verifyContentExtensionFromTrustedState, verifyCountersignature, verifyCreditClaim, verifyCreditEntry, verifyIdentityChain, verifyIdentityExtensionFromTrustedState, verifyRevocation, verifySignRequest };
563
+ export { type AnchorKind, ArtifactPayload, ContentOperation, CountersignPayload, type CreditClaimFailureReason, CreditClaimVerifyError, type CreditEntry, type CreditEntryState, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, RECOGNIZED_SERVICE_TYPES, ServiceEntry, type SignRequestFailureReason, SignRequestVerifyError, Signer, type VerifiedArtifact, type VerifiedContentChain, type VerifiedCountersignature, type VerifiedCreditClaim, type VerifiedCreditEntry, VerifiedIdentity, type VerifiedRevocation, type VerifiedSignRequest, anchorsByLabel, assertCanonicalSignRequestPayload, assertServicesWithinCap, buildSignRequest, classifyAnchor, decodeEd25519PublicMultikey, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, isRecognizedServiceType, relayEndpoints, signArtifact, signContentOperation, signCountersignature, signCreditClaim, signIdentityOperation, signRevocation, verifyArtifact, verifyContentChain, verifyContentExtensionFromTrustedState, verifyCountersignature, verifyCreditClaim, verifyCreditEntry, verifyIdentityChain, verifyIdentityExtensionFromTrustedState, verifyRevocation, verifySignRequest };
@@ -52,16 +52,17 @@ import {
52
52
  verifyIdentityExtensionFromTrustedState,
53
53
  verifyRevocation,
54
54
  verifySignRequest
55
- } from "../chunk-LMAPBTDW.js";
56
- import "../chunk-T2YR5KWQ.js";
57
- import "../chunk-JVSC67DC.js";
55
+ } from "../chunk-Q7FREWMY.js";
56
+ import "../chunk-FZHHSC5N.js";
57
+ import "../chunk-BMIOY5W5.js";
58
58
  import {
59
59
  ED25519_PRIV_MULTICODEC,
60
60
  ED25519_PUB_MULTICODEC,
61
+ decodeEd25519PublicMultikey,
61
62
  decodeMultikey,
62
63
  encodeEd25519Multikey
63
- } from "../chunk-IDVYITX7.js";
64
- import "../chunk-4LG2GEB2.js";
64
+ } from "../chunk-F7RHI2CK.js";
65
+ import "../chunk-KAW4MT4P.js";
65
66
  export {
66
67
  ARTIFACT_CID_ANCHOR_RE,
67
68
  ArtifactPayload,
@@ -97,6 +98,7 @@ export {
97
98
  assertServicesWithinCap,
98
99
  buildSignRequest,
99
100
  classifyAnchor,
101
+ decodeEd25519PublicMultikey,
100
102
  decodeMultikey,
101
103
  deriveChainIdentifier,
102
104
  deriveContentId,
@@ -1,8 +1,7 @@
1
1
  import {
2
- ED25519_PUB_MULTICODEC,
3
- decodeMultikey,
2
+ decodeEd25519PublicMultikey,
4
3
  encodeEd25519Multikey
5
- } from "./chunk-IDVYITX7.js";
4
+ } from "./chunk-F7RHI2CK.js";
6
5
  import {
7
6
  base64urlDecode,
8
7
  base64urlEncode,
@@ -10,7 +9,7 @@ import {
10
9
  isValidEd25519Signature,
11
10
  sha256,
12
11
  signPayloadEd25519
13
- } from "./chunk-4LG2GEB2.js";
12
+ } from "./chunk-KAW4MT4P.js";
14
13
 
15
14
  // src/key-proof/role-set.ts
16
15
  var KEY_ROLES = ["auth", "assert", "controller"];
@@ -205,11 +204,7 @@ var decodeKeyProof = (jws, expectedTyp) => {
205
204
  var verifyKeyProofSignature = (decoded) => {
206
205
  let keyBytes;
207
206
  try {
208
- const key = decodeMultikey(decoded.payload.publicKeyMultibase);
209
- if (key.codec !== ED25519_PUB_MULTICODEC) {
210
- throw new Error("publicKeyMultibase is not an Ed25519 public key");
211
- }
212
- keyBytes = key.keyBytes;
207
+ keyBytes = decodeEd25519PublicMultikey(decoded.payload.publicKeyMultibase);
213
208
  } catch (err) {
214
209
  throw invalid(
215
210
  "signature",
@@ -36,10 +36,18 @@ var decodeMultikey = (multibase) => {
36
36
  `unsupported multikey codec: [0x${bytes[0]?.toString(16)}, 0x${bytes[1]?.toString(16)}]`
37
37
  );
38
38
  };
39
+ var decodeEd25519PublicMultikey = (multibase) => {
40
+ const { keyBytes, codec } = decodeMultikey(multibase);
41
+ if (codec !== ED25519_PUB_MULTICODEC) {
42
+ throw new Error("publicKeyMultibase is not an Ed25519 public key");
43
+ }
44
+ return keyBytes;
45
+ };
39
46
 
40
47
  export {
41
48
  ED25519_PUB_MULTICODEC,
42
49
  ED25519_PRIV_MULTICODEC,
43
50
  encodeEd25519Multikey,
44
- decodeMultikey
51
+ decodeMultikey,
52
+ decodeEd25519PublicMultikey
45
53
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
- decodeMultikey
3
- } from "./chunk-IDVYITX7.js";
2
+ decodeEd25519PublicMultikey
3
+ } from "./chunk-F7RHI2CK.js";
4
4
  import {
5
5
  assertJwsProfile,
6
6
  base64urlDecode,
@@ -10,7 +10,7 @@ import {
10
10
  decodeJwsUnsafe,
11
11
  sha256,
12
12
  verifyJws
13
- } from "./chunk-4LG2GEB2.js";
13
+ } from "./chunk-KAW4MT4P.js";
14
14
 
15
15
  // src/credentials/schemas.ts
16
16
  import { z } from "zod";
@@ -355,7 +355,10 @@ var verifyProofEnvelope = async (input, shape, resolvePresenter) => {
355
355
  const key = state.keys.find((candidate) => candidate.id === presenterKeyId);
356
356
  if (!key) throw invalidProof(`${shape.label} signing key is not a current key of the presenter`);
357
357
  try {
358
- verifyJws({ token: input.proof, publicKey: decodeMultikey(key.publicKeyMultibase).keyBytes });
358
+ verifyJws({
359
+ token: input.proof,
360
+ publicKey: decodeEd25519PublicMultikey(key.publicKeyMultibase)
361
+ });
359
362
  } catch (err) {
360
363
  throw invalidProof(err instanceof Error ? err.message : `invalid ${shape.label} signature`);
361
364
  }
@@ -382,7 +385,7 @@ var resolveKeyFromIdentity = (identity, kid, determinate) => {
382
385
  );
383
386
  throw determinate ? miss : markDependencyMissing(miss);
384
387
  }
385
- const { keyBytes } = decodeMultikey(key.publicKeyMultibase);
388
+ const keyBytes = decodeEd25519PublicMultikey(key.publicKeyMultibase);
386
389
  return keyBytes;
387
390
  };
388
391
  var basisUnixSeconds = (basis) => {
@@ -435,6 +438,7 @@ var verifyDFOSCredential = async (jwsToken, options) => {
435
438
  }
436
439
  const payload = result.data;
437
440
  const kid = decoded.header.kid;
441
+ if (kid === void 0) throw new CredentialVerificationError("credential kid must be present");
438
442
  const hashIdx = kid.indexOf("#");
439
443
  if (hashIdx < 0) throw new CredentialVerificationError("credential kid must be a DID URL");
440
444
  const kidDid = kid.substring(0, hashIdx);
@@ -456,7 +460,7 @@ var verifyDFOSCredential = async (jwsToken, options) => {
456
460
  } catch {
457
461
  throw new CredentialVerificationError("invalid credential signature");
458
462
  }
459
- const encoded = await dagCborCanonicalEncode(payload);
463
+ const encoded = await dagCborCanonicalEncode(decoded.payload);
460
464
  const credentialCID = encoded.cid.toString();
461
465
  if (!decoded.header.cid) {
462
466
  throw new CredentialVerificationError("missing cid in credential header");
@@ -534,8 +538,9 @@ var parseResource = (resource) => {
534
538
  if (colonIdx < 0) return null;
535
539
  return { type: resource.substring(0, colonIdx), id: resource.substring(colonIdx + 1) };
536
540
  };
541
+ var ASCII_TRIM_RE = /^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g;
537
542
  var parseActions = (action) => new Set(
538
- action.split(",").map((a) => a.trim()).filter((a) => a !== "")
543
+ action.split(",").map((a) => a.replace(ASCII_TRIM_RE, "")).filter((a) => a !== "")
539
544
  );
540
545
  var isAttenuated = (parentAtt, childAtt) => {
541
546
  return childAtt.every((childEntry) => {
@@ -592,7 +597,7 @@ var matchesResource = async (att, resource, action) => {
592
597
  };
593
598
  var decodeDFOSCredentialUnsafe = (jwsToken) => {
594
599
  const decoded = decodeJwsUnsafe(jwsToken);
595
- if (!decoded) return null;
600
+ if (!decoded || decoded.header.kid === void 0) return null;
596
601
  const result = DFOSCredentialPayload.safeParse(decoded.payload);
597
602
  if (!result.success) return null;
598
603
  return {
@@ -79,6 +79,68 @@ var normalizedId = (prefix, id) => {
79
79
  return `${prefixLowered}_${idLowered}`;
80
80
  };
81
81
 
82
+ // src/crypto/json-scan.ts
83
+ var assertCanonicalJsonText = (text) => {
84
+ const frames = [];
85
+ let i = 0;
86
+ while (i < text.length) {
87
+ const ch = text[i];
88
+ if (ch === '"') {
89
+ const end = scanStringToken(text, i);
90
+ if (end < 0) return;
91
+ const value = decodeStringToken(text.slice(i, end));
92
+ if (value === null) return;
93
+ if (LONE_SURROGATE_RE.test(value)) {
94
+ throw new Error("string with an unpaired surrogate is not canonicalizable");
95
+ }
96
+ const frame = frames[frames.length - 1];
97
+ if (frame && frame.isObject && frame.awaitingKey) {
98
+ if (frame.keys.has(value)) {
99
+ throw new Error(`duplicate JSON key is malformed: ${JSON.stringify(value)}`);
100
+ }
101
+ frame.keys.add(value);
102
+ frame.awaitingKey = false;
103
+ }
104
+ i = end;
105
+ continue;
106
+ }
107
+ if (ch === "{") {
108
+ frames.push({ isObject: true, keys: /* @__PURE__ */ new Set(), awaitingKey: true });
109
+ } else if (ch === "[") {
110
+ frames.push({ isObject: false, keys: /* @__PURE__ */ new Set(), awaitingKey: false });
111
+ } else if (ch === "}" || ch === "]") {
112
+ frames.pop();
113
+ } else if (ch === ",") {
114
+ const frame = frames[frames.length - 1];
115
+ if (frame && frame.isObject) frame.awaitingKey = true;
116
+ }
117
+ i++;
118
+ }
119
+ };
120
+ var scanStringToken = (text, start) => {
121
+ let i = start + 1;
122
+ while (i < text.length) {
123
+ const ch = text[i];
124
+ if (ch === "\\") {
125
+ i += 2;
126
+ continue;
127
+ }
128
+ if (ch === '"') return i + 1;
129
+ i++;
130
+ }
131
+ return -1;
132
+ };
133
+ var decodeStringToken = (raw) => {
134
+ if (!raw.includes("\\")) return raw.slice(1, -1);
135
+ try {
136
+ const value = JSON.parse(raw);
137
+ return typeof value === "string" ? value : null;
138
+ } catch {
139
+ return null;
140
+ }
141
+ };
142
+ var LONE_SURROGATE_RE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
143
+
82
144
  // src/crypto/jws-profile.ts
83
145
  var assertJwsProfile = (header, makeError) => {
84
146
  if (header.alg !== "EdDSA") {
@@ -96,6 +158,30 @@ var assertJwsProfile = (header, makeError) => {
96
158
  };
97
159
 
98
160
  // src/crypto/jws.ts
161
+ var decodeJwsSegment = (segmentB64) => {
162
+ let text;
163
+ try {
164
+ text = new TextDecoder("utf-8", { fatal: true }).decode(base64urlDecode(segmentB64));
165
+ } catch {
166
+ return null;
167
+ }
168
+ assertCanonicalJsonText(text);
169
+ let value;
170
+ try {
171
+ value = JSON.parse(text);
172
+ } catch {
173
+ return null;
174
+ }
175
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
176
+ return value;
177
+ };
178
+ var asJwsHeader = (raw) => {
179
+ if (typeof raw["alg"] !== "string") return null;
180
+ if (typeof raw["typ"] !== "string") return null;
181
+ if ("kid" in raw && typeof raw["kid"] !== "string") return null;
182
+ if ("cid" in raw && typeof raw["cid"] !== "string") return null;
183
+ return raw;
184
+ };
99
185
  var createJws = async (options) => {
100
186
  const headerB64 = base64urlEncode(JSON.stringify(options.header));
101
187
  const payloadB64 = base64urlEncode(JSON.stringify(options.payload));
@@ -111,18 +197,22 @@ var verifyJws = (options) => {
111
197
  throw new JwsVerificationError("Invalid token format");
112
198
  }
113
199
  const [headerB64, payloadB64, signatureB64] = parts;
114
- let header;
200
+ let rawHeader;
115
201
  let payload;
116
202
  try {
117
- header = JSON.parse(new TextDecoder().decode(base64urlDecode(headerB64)));
118
- payload = JSON.parse(new TextDecoder().decode(base64urlDecode(payloadB64)));
119
- } catch {
203
+ rawHeader = decodeJwsSegment(headerB64);
204
+ payload = decodeJwsSegment(payloadB64);
205
+ } catch (e) {
206
+ throw new JwsVerificationError(e.message);
207
+ }
208
+ if (!rawHeader || !payload) {
120
209
  throw new JwsVerificationError("Failed to decode token");
121
210
  }
122
- assertJwsProfile(
123
- header,
124
- (m) => new JwsVerificationError(m)
125
- );
211
+ assertJwsProfile(rawHeader, (m) => new JwsVerificationError(m));
212
+ const header = asJwsHeader(rawHeader);
213
+ if (!header) {
214
+ throw new JwsVerificationError("Invalid protected header");
215
+ }
126
216
  const signingInput = `${headerB64}.${payloadB64}`;
127
217
  const signingInputBytes = new TextEncoder().encode(signingInput);
128
218
  const signatureBytes = base64urlDecode(signatureB64);
@@ -135,14 +225,19 @@ var verifyJws = (options) => {
135
225
  var decodeJwsUnsafe = (token) => {
136
226
  const parts = token.split(".");
137
227
  if (parts.length !== 3) return null;
228
+ const [headerB64, payloadB64] = parts;
229
+ let rawHeader;
230
+ let payload;
138
231
  try {
139
- const [headerB64, payloadB64] = parts;
140
- const header = JSON.parse(new TextDecoder().decode(base64urlDecode(headerB64)));
141
- const payload = JSON.parse(new TextDecoder().decode(base64urlDecode(payloadB64)));
142
- return { header, payload };
232
+ rawHeader = decodeJwsSegment(headerB64);
233
+ payload = decodeJwsSegment(payloadB64);
143
234
  } catch {
144
235
  return null;
145
236
  }
237
+ if (!rawHeader || !payload) return null;
238
+ const header = asJwsHeader(rawHeader);
239
+ if (!header) return null;
240
+ return { header, payload };
146
241
  };
147
242
  var JwsVerificationError = class extends Error {
148
243
  constructor(message) {
@@ -203,7 +298,7 @@ var verifyJwt = (options) => {
203
298
  const isValid = isValidEd25519Signature(signingInputBytes, signatureBytes, options.publicKey);
204
299
  if (!isValid) throw new JwtVerificationError("Invalid signature");
205
300
  const currentTime = options.currentTime ?? Math.floor(Date.now() / 1e3);
206
- if (payload.exp <= currentTime) {
301
+ if (!Number.isSafeInteger(payload.exp) || payload.exp <= currentTime) {
207
302
  throw new JwtVerificationError("Token expired");
208
303
  }
209
304
  if (options.issuer !== void 0 && payload.iss !== options.issuer) {
@@ -231,20 +326,25 @@ import * as Block from "multiformats/block";
231
326
  import { CID } from "multiformats/cid";
232
327
  import { sha256 as sha2562 } from "multiformats/hashes/sha2";
233
328
  var dagCborCanonicalEncode = async (value) => {
234
- assertCanonicalNumbers(value);
329
+ assertCanonicalValue(value);
235
330
  const serialized = JSON.parse(JSON.stringify(value));
236
- assertCanonicalNumbers(serialized);
237
- return await Block.encode({
238
- // removes any undefineds or other non-serializable values (and normalizes
239
- // -0 to 0)
240
- value: serialized,
241
- codec: dagCborCodec,
242
- hasher: sha2562
243
- });
331
+ assertCanonicalValue(serialized);
332
+ try {
333
+ return await Block.encode({
334
+ // removes any undefineds or other non-serializable values (and normalizes
335
+ // -0 to 0)
336
+ value: serialized,
337
+ codec: dagCborCodec,
338
+ hasher: sha2562
339
+ });
340
+ } catch (e) {
341
+ throw new Error(`value is not canonically encodable: ${e.message}`);
342
+ }
244
343
  };
245
344
  var MAX_SAFE_CANONICAL_INTEGER = 9007199254740991;
246
345
  var MAX_CANONICAL_DEPTH = 1024;
247
- var assertCanonicalNumbers = (value, depth = 0) => {
346
+ var LONE_SURROGATE_RE2 = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
347
+ var assertCanonicalValue = (value, depth = 0) => {
248
348
  if (depth > MAX_CANONICAL_DEPTH) {
249
349
  throw new Error(`value nesting exceeds max depth ${MAX_CANONICAL_DEPTH}`);
250
350
  }
@@ -264,12 +364,26 @@ var assertCanonicalNumbers = (value, depth = 0) => {
264
364
  }
265
365
  return;
266
366
  }
367
+ if (typeof value === "string") {
368
+ if (LONE_SURROGATE_RE2.test(value)) {
369
+ throw new Error("string with an unpaired surrogate is not canonicalizable");
370
+ }
371
+ return;
372
+ }
267
373
  if (Array.isArray(value)) {
268
- for (const entry of value) assertCanonicalNumbers(entry, depth + 1);
374
+ for (const entry of value) assertCanonicalValue(entry, depth + 1);
269
375
  return;
270
376
  }
271
377
  if (value !== null && typeof value === "object") {
272
- for (const entry of Object.values(value)) assertCanonicalNumbers(entry, depth + 1);
378
+ if ("/" in value && "bytes" in value) {
379
+ throw new Error('object carrying both "/" and "bytes" members is not canonicalizable');
380
+ }
381
+ for (const [key, entry] of Object.entries(value)) {
382
+ if (LONE_SURROGATE_RE2.test(key)) {
383
+ throw new Error("string with an unpaired surrogate is not canonicalizable");
384
+ }
385
+ assertCanonicalValue(entry, depth + 1);
386
+ }
273
387
  }
274
388
  };
275
389
  var parseDagCborCID = (cid) => {
@@ -293,6 +407,7 @@ export {
293
407
  generateId,
294
408
  isValidId,
295
409
  normalizedId,
410
+ assertCanonicalJsonText,
296
411
  assertJwsProfile,
297
412
  createJws,
298
413
  verifyJws,
@@ -6,16 +6,16 @@ import {
6
6
  matchesResource,
7
7
  verifyDFOSCredential,
8
8
  verifyDelegationChain
9
- } from "./chunk-T2YR5KWQ.js";
9
+ } from "./chunk-FZHHSC5N.js";
10
10
  import {
11
11
  KEY_ADD_JWS_TYP,
12
12
  KEY_ROLES,
13
13
  unsafeKeyProofSubject,
14
14
  verifyChainKeyProof
15
- } from "./chunk-JVSC67DC.js";
15
+ } from "./chunk-BMIOY5W5.js";
16
16
  import {
17
- decodeMultikey
18
- } from "./chunk-IDVYITX7.js";
17
+ decodeEd25519PublicMultikey
18
+ } from "./chunk-F7RHI2CK.js";
19
19
  import {
20
20
  assertJwsProfile,
21
21
  base64urlDecode,
@@ -25,13 +25,13 @@ import {
25
25
  decodeJwsUnsafe,
26
26
  generateIdNoPrefix,
27
27
  verifyJws
28
- } from "./chunk-4LG2GEB2.js";
28
+ } from "./chunk-KAW4MT4P.js";
29
29
 
30
30
  // src/chain/schemas.ts
31
31
  import { z } from "zod";
32
32
  var MAX_KEYS_PER_ROLE = 256;
33
33
  var MAX_KEY_PROOFS = 256;
34
- var MAX_RELATION = 64;
34
+ var MAX_RELATION_BYTES = 64;
35
35
  var MAX_SERVICES_ENTRIES = 256;
36
36
  var MAX_SERVICES_PAYLOAD_SIZE = 32768;
37
37
  var MAX_OPERATION_SIZE = 65536;
@@ -78,7 +78,7 @@ var parseProtocolTimestampUnix = (value) => {
78
78
  const ms = Date.parse(value);
79
79
  return Number.isFinite(ms) ? Math.floor(ms / 1e3) : null;
80
80
  };
81
- var CIDString = z.string();
81
+ var CIDString = z.string().min(1, "CID must not be empty");
82
82
  var IdentityCreate = z.looseObject({
83
83
  version: z.literal(1),
84
84
  type: z.literal("create"),
@@ -261,7 +261,9 @@ var CountersignPayload = z.looseObject({
261
261
  type: z.literal("countersign"),
262
262
  did: z.string(),
263
263
  targetCID: CIDString,
264
- relation: z.string().min(1).max(MAX_RELATION).optional(),
264
+ relation: z.string().min(1).refine((value) => new TextEncoder().encode(value).length <= MAX_RELATION_BYTES, {
265
+ message: `relation exceeds ${MAX_RELATION_BYTES} bytes`
266
+ }).optional(),
265
267
  createdAt: Iso8601
266
268
  });
267
269
  var RevocationPayload = z.looseObject({
@@ -544,7 +546,7 @@ var verifyIdentityChain = async (input) => {
544
546
  }
545
547
  }
546
548
  }
547
- const encoded = await dagCborCanonicalEncode(op);
549
+ const encoded = await dagCborCanonicalEncode(decoded.payload);
548
550
  if (encoded.bytes.length > MAX_OPERATION_SIZE) {
549
551
  throw new Error(
550
552
  `log[${idx}]: operation exceeds max size: ${encoded.bytes.length} > ${MAX_OPERATION_SIZE}`
@@ -558,6 +560,7 @@ var verifyIdentityChain = async (input) => {
558
560
  throw new Error(`log[${idx}]: cid mismatch in protected header`);
559
561
  }
560
562
  const kid = decoded.header.kid;
563
+ if (kid === void 0) throw new Error(`log[${idx}]: kid must be present`);
561
564
  let signingKeyId;
562
565
  if (kid.includes("#")) {
563
566
  const hashIdx = kid.indexOf("#");
@@ -575,7 +578,7 @@ var verifyIdentityChain = async (input) => {
575
578
  if (!signingKey) {
576
579
  throw new Error(`log[${idx}]: kid references unknown key: ${signingKeyId}`);
577
580
  }
578
- const { keyBytes } = decodeMultikey(signingKey.publicKeyMultibase);
581
+ const keyBytes = decodeEd25519PublicMultikey(signingKey.publicKeyMultibase);
579
582
  try {
580
583
  verifyJws({ token: jwsToken, publicKey: keyBytes });
581
584
  } catch {
@@ -694,7 +697,7 @@ var verifyIdentityExtensionFromTrustedState = async (input) => {
694
697
  if (op.createdAt <= lastCreatedAt) {
695
698
  throw new Error("createdAt must be after last op");
696
699
  }
697
- const encoded = await dagCborCanonicalEncode(op);
700
+ const encoded = await dagCborCanonicalEncode(decoded.payload);
698
701
  if (encoded.bytes.length > MAX_OPERATION_SIZE) {
699
702
  throw new Error(`operation exceeds max size: ${encoded.bytes.length} > ${MAX_OPERATION_SIZE}`);
700
703
  }
@@ -702,6 +705,7 @@ var verifyIdentityExtensionFromTrustedState = async (input) => {
702
705
  if (!decoded.header.cid) throw new Error("missing cid in protected header");
703
706
  if (decoded.header.cid !== operationCID) throw new Error("cid mismatch in protected header");
704
707
  const kid = decoded.header.kid;
708
+ if (kid === void 0) throw new Error("kid must be present");
705
709
  if (!kid.includes("#")) {
706
710
  throw new Error("non-genesis op kid must be DID URL, got bare key ID");
707
711
  }
@@ -715,7 +719,7 @@ var verifyIdentityExtensionFromTrustedState = async (input) => {
715
719
  if (!signingKey) {
716
720
  throw new Error(`kid references unknown key: ${signingKeyId}`);
717
721
  }
718
- const { keyBytes } = decodeMultikey(signingKey.publicKeyMultibase);
722
+ const keyBytes = decodeEd25519PublicMultikey(signingKey.publicKeyMultibase);
719
723
  try {
720
724
  verifyJws({ token: newOp, publicKey: keyBytes });
721
725
  } catch {
@@ -791,11 +795,13 @@ var verifyIdentityExtensionFromTrustedState = async (input) => {
791
795
 
792
796
  // src/chain/content-chain.ts
793
797
  var operationSizeForCap = async (op, fullByteLength) => {
798
+ if (op.type === "create") return fullByteLength;
794
799
  const auth = op.authorization;
795
800
  if (typeof auth !== "string") return fullByteLength;
796
- if (auth.length > MAX_CREDENTIAL_SIZE) {
801
+ const authByteLength = new TextEncoder().encode(auth).length;
802
+ if (authByteLength > MAX_CREDENTIAL_SIZE) {
797
803
  throw new Error(
798
- `authorization credential exceeds max size: ${auth.length} > ${MAX_CREDENTIAL_SIZE}`
804
+ `authorization credential exceeds max size: ${authByteLength} > ${MAX_CREDENTIAL_SIZE}`
799
805
  );
800
806
  }
801
807
  const { authorization: _omit, ...rest } = op;
@@ -888,6 +894,7 @@ var verifyContentChain = async (input) => {
888
894
  }
889
895
  }
890
896
  const kid = decoded.header.kid;
897
+ if (kid === void 0) throw new Error(`log[${idx}]: kid must be present`);
891
898
  const hashIdx = kid.indexOf("#");
892
899
  if (hashIdx < 0) throw new Error(`log[${idx}]: kid must be a DID URL`);
893
900
  const kidDid = kid.substring(0, hashIdx);
@@ -927,8 +934,8 @@ var verifyContentChain = async (input) => {
927
934
  );
928
935
  }
929
936
  }
930
- const encoded = await dagCborCanonicalEncode(op);
931
- const opSize = await operationSizeForCap(op, encoded.bytes.length);
937
+ const encoded = await dagCborCanonicalEncode(decoded.payload);
938
+ const opSize = await operationSizeForCap(decoded.payload, encoded.bytes.length);
932
939
  if (opSize > MAX_OPERATION_SIZE) {
933
940
  throw new Error(`operation exceeds max size: ${opSize} > ${MAX_OPERATION_SIZE}`);
934
941
  }
@@ -998,6 +1005,7 @@ var verifyContentExtensionFromTrustedState = async (input) => {
998
1005
  throw new Error("createdAt must be after last op");
999
1006
  }
1000
1007
  const kid = decoded.header.kid;
1008
+ if (kid === void 0) throw new Error("kid must be present");
1001
1009
  const hashIdx = kid.indexOf("#");
1002
1010
  if (hashIdx < 0) throw new Error("kid must be a DID URL");
1003
1011
  const kidDid = kid.substring(0, hashIdx);
@@ -1032,8 +1040,8 @@ var verifyContentExtensionFromTrustedState = async (input) => {
1032
1040
  throw carryDependencyMissing(err, new Error(`authorization verification failed: ${message}`));
1033
1041
  }
1034
1042
  }
1035
- const encoded = await dagCborCanonicalEncode(op);
1036
- const opSize = await operationSizeForCap(op, encoded.bytes.length);
1043
+ const encoded = await dagCborCanonicalEncode(decoded.payload);
1044
+ const opSize = await operationSizeForCap(decoded.payload, encoded.bytes.length);
1037
1045
  if (opSize > MAX_OPERATION_SIZE) {
1038
1046
  throw new Error(`operation exceeds max size: ${opSize} > ${MAX_OPERATION_SIZE}`);
1039
1047
  }
@@ -1076,6 +1084,7 @@ var verifyCountersignature = async (input) => {
1076
1084
  }
1077
1085
  const payload = result.data;
1078
1086
  const kid = decoded.header.kid;
1087
+ if (kid === void 0) throw new Error("kid must be present");
1079
1088
  const hashIdx = kid.indexOf("#");
1080
1089
  if (hashIdx < 0) throw new Error("countersignature kid must be a DID URL");
1081
1090
  const kidDid = kid.substring(0, hashIdx);
@@ -1129,6 +1138,7 @@ var verifyArtifact = async (input) => {
1129
1138
  throw new Error(`invalid artifact typ: ${decoded.header.typ}`);
1130
1139
  }
1131
1140
  const kid = decoded.header.kid;
1141
+ if (kid === void 0) throw new Error("kid must be present");
1132
1142
  const hashIdx = kid.indexOf("#");
1133
1143
  if (hashIdx < 0) throw new Error("artifact kid must be a DID URL");
1134
1144
  const kidDid = kid.substring(0, hashIdx);
@@ -1186,6 +1196,7 @@ var verifyRevocation = async (input) => {
1186
1196
  throw new Error(`invalid revocation typ: ${decoded.header.typ}`);
1187
1197
  }
1188
1198
  const kid = decoded.header.kid;
1199
+ if (kid === void 0) throw new Error("kid must be present");
1189
1200
  const hashIdx = kid.indexOf("#");
1190
1201
  if (hashIdx < 0) throw new Error("revocation kid must be a DID URL");
1191
1202
  const kidDid = kid.substring(0, hashIdx);
@@ -1198,7 +1209,7 @@ var verifyRevocation = async (input) => {
1198
1209
  } catch {
1199
1210
  throw new Error("invalid revocation signature");
1200
1211
  }
1201
- const encoded = await dagCborCanonicalEncode(payload);
1212
+ const encoded = await dagCborCanonicalEncode(decoded.payload);
1202
1213
  const revocationCID = encoded.cid.toString();
1203
1214
  if (!decoded.header.cid) throw new Error("missing cid in revocation header");
1204
1215
  if (decoded.header.cid !== revocationCID) throw new Error("revocation cid mismatch");
@@ -1231,7 +1242,7 @@ var resolveKeyFromIdentity = (identity, kid) => {
1231
1242
  if (!key) {
1232
1243
  throw markDependencyMissing(invalid(`key ${keyId} not found on identity ${identity.did}`));
1233
1244
  }
1234
- const { keyBytes } = decodeMultikey(key.publicKeyMultibase);
1245
+ const keyBytes = decodeEd25519PublicMultikey(key.publicKeyMultibase);
1235
1246
  return keyBytes;
1236
1247
  };
1237
1248
  var normalizeClaimCreatedAt = (createdAt) => {
@@ -1291,8 +1302,7 @@ var verifyCreditClaim = async (jwsToken, options) => {
1291
1302
  }
1292
1303
  const decoded = decodeJwsUnsafe(jwsToken);
1293
1304
  if (!decoded) throw invalid("failed to decode credit claim JWS");
1294
- const rawHeader = decoded.header;
1295
- if (typeof rawHeader["typ"] !== "string" || typeof rawHeader["kid"] !== "string") {
1305
+ if (typeof decoded.header.kid !== "string") {
1296
1306
  throw invalid("credit claim header must carry a string typ and kid");
1297
1307
  }
1298
1308
  if (decoded.header.typ !== "did:dfos:credit-claim") {
@@ -1327,7 +1337,7 @@ var verifyCreditClaim = async (jwsToken, options) => {
1327
1337
  } catch {
1328
1338
  throw invalid("invalid credit claim signature");
1329
1339
  }
1330
- const encoded = await dagCborCanonicalEncode(payload);
1340
+ const encoded = await dagCborCanonicalEncode(decoded.payload);
1331
1341
  const claimCID = encoded.cid.toString();
1332
1342
  if (!decoded.header.cid) throw invalid("missing cid in credit claim header");
1333
1343
  if (decoded.header.cid !== claimCID) throw invalid("credit claim cid mismatch");
@@ -1454,7 +1464,7 @@ var resolveCurrentKey = (identity, kid) => {
1454
1464
  );
1455
1465
  if (!key) throw invalid2(`key ${keyId} not found on current identity ${identity.did}`);
1456
1466
  try {
1457
- return decodeMultikey(key.publicKeyMultibase).keyBytes;
1467
+ return decodeEd25519PublicMultikey(key.publicKeyMultibase);
1458
1468
  } catch {
1459
1469
  throw invalid2(`key ${keyId} on identity ${identity.did} is not a valid Ed25519 multikey`);
1460
1470
  }
@@ -1518,7 +1528,7 @@ var verifySignRequest = async (jwsToken, options) => {
1518
1528
  throw invalid2("sign request protected header must be an object");
1519
1529
  }
1520
1530
  assertJwsProfile(rawHeader, invalid2);
1521
- if (typeof rawHeader["typ"] !== "string" || typeof rawHeader["kid"] !== "string") {
1531
+ if (typeof rawHeader["typ"] !== "string" || typeof decoded.header.kid !== "string") {
1522
1532
  throw invalid2("sign request header must carry a string typ and kid");
1523
1533
  }
1524
1534
  if (decoded.header.typ !== "did:dfos:sign-request") {
@@ -1554,7 +1564,7 @@ var verifySignRequest = async (jwsToken, options) => {
1554
1564
  }
1555
1565
  let encoded;
1556
1566
  try {
1557
- encoded = await dagCborCanonicalEncode(payload);
1567
+ encoded = await dagCborCanonicalEncode(decoded.payload);
1558
1568
  } catch (error) {
1559
1569
  const detail = error instanceof Error ? error.message : String(error);
1560
1570
  throw invalid2(`failed to derive sign request CID: ${detail}`);
@@ -1,15 +1,16 @@
1
- export { b as Attenuation, e as CredentialVerificationError, D as DFOSCredentialPayload, i as MAX_CREDENTIAL_SIZE, R as ResolvedIdentity, r as RevocationChecker, V as VerifiedDFOSCredential, w as VerifiedDelegationChain, z as createDFOSCredential, B as decodeDFOSCredentialUnsafe, E as isAttenuated, F as matchesResource, H as verifyDFOSCredential, J as verifyDelegationChain } from '../dfos-credential-CtG0_n4P.js';
1
+ export { b as Attenuation, e as CredentialVerificationError, D as DFOSCredentialPayload, i as MAX_CREDENTIAL_SIZE, R as ResolvedIdentity, r as RevocationChecker, V as VerifiedDFOSCredential, w as VerifiedDelegationChain, z as createDFOSCredential, B as decodeDFOSCredentialUnsafe, E as isAttenuated, F as matchesResource, H as verifyDFOSCredential, J as verifyDelegationChain } from '../dfos-credential-j-kHtFvG.js';
2
2
  import 'zod';
3
3
 
4
4
  /**
5
- * The normative JWS header `typ` for a request proof (API-AUTH.md). Signers MUST
6
- * set it; the request-proof verifier rejects anything else — it is also what lets
7
- * typ-routing dispatchers tell a proof apart from credentials and chain ops.
5
+ * The normative JWS header `typ` for a request proof (INTEGRATIONS.md, The
6
+ * request proof). Signers MUST set it; the request-proof verifier rejects
7
+ * anything else — it is also what lets typ-routing dispatchers tell a proof
8
+ * apart from credentials and chain ops.
8
9
  */
9
10
  declare const REQUEST_PROOF_JWS_TYP = "did:dfos:request-proof";
10
11
  /**
11
- * The normative JWS header `typ` for an identity proof (API-AUTH.md) — the
12
- * request proof's credential-less sibling.
12
+ * The normative JWS header `typ` for an identity proof (INTEGRATIONS.md, The
13
+ * identity proof) — the request proof's credential-less sibling.
13
14
  *
14
15
  * THE TYP GATE IS ABSOLUTE, IN BOTH DIRECTIONS. "Possession of a grant's
15
16
  * audience key" and "possession of a bare identity's key" are different claims,
@@ -84,10 +85,10 @@ interface IdentityProofPayload {
84
85
  /**
85
86
  * ADDITIVE MEMBERS, appended AFTER the canonical order.
86
87
  *
87
- * API-AUTH.md's growth rule is additive members on this envelope, never a new
88
- * envelope: "additional members register additively, appended to the canonical
89
- * order". `jti` — the per-request uniqueness member a write-gating deployment
90
- * requires — is the named one.
88
+ * INTEGRATIONS.md, One envelope, optional credential's growth rule is additive
89
+ * members on this envelope, never a new envelope: "additional members register
90
+ * additively, appended to the canonical order". `jti` — the per-request
91
+ * uniqueness member a write-gating deployment requires — is the named one.
91
92
  *
92
93
  * TWO RULES MAKE THIS A BYTE-TWIN. (1) Extra members are emitted AFTER every
93
94
  * canonical member, so a verifier that ignores them still reconstructs the same
@@ -173,7 +174,7 @@ interface SignApiRequestInput {
173
174
  /**
174
175
  * ADDITIVE members, appended after the canonical order in lexicographic name
175
176
  * order. `{ jti }` is the registered one — required by a deployment that gates
176
- * WRITES with this envelope (API-AUTH.md, Security Considerations).
177
+ * WRITES with this envelope (INTEGRATIONS.md, API security notes).
177
178
  */
178
179
  extraMembers?: ProofExtraMembers;
179
180
  }
@@ -211,7 +212,7 @@ interface SignApiIdentityRequestInput {
211
212
  /**
212
213
  * ADDITIVE members, appended after the canonical order in lexicographic name
213
214
  * order. `{ jti }` is the registered one, and a WRITE-SHAPED surface — relay
214
- * ingestion, blob upload — REQUIRES it (WEB-RELAY.md, Authentication).
215
+ * ingestion, blob upload — REQUIRES it (INTEGRATIONS.md, API security notes).
215
216
  */
216
217
  extraMembers?: ProofExtraMembers;
217
218
  }
@@ -247,8 +248,8 @@ declare const buildApiAuthHeaders: (input: {
247
248
  * On an `api:<host>` surface an accompanying `X-Credential` is MALFORMED: the
248
249
  * two headers would assert two different claims at once. A relay content-plane
249
250
  * read is NOT that case — there the identity proof is the AuthN half and a DFOS
250
- * credential presentation is a separate authorization artifact (WEB-RELAY.md,
251
- * Authentication) — so that refusal belongs to the middleware of the surface
251
+ * credential presentation is a separate authorization artifact (RELAY.md,
252
+ * Access) — so that refusal belongs to the middleware of the surface
252
253
  * being served, never to this builder.
253
254
  */
254
255
  declare const buildApiIdentityHeaders: (input: {
@@ -317,7 +318,7 @@ interface ProofPresenterState {
317
318
  type ResolveProofPresenter = (did: string) => Promise<ProofPresenterState | null>;
318
319
  /**
319
320
  * What the PROOF PHASE reads — the subset of a verifier's inputs that
320
- * API-AUTH.md steps 1–7 touch.
321
+ * INTEGRATIONS.md, Verification algorithm steps 1–7 touch.
321
322
  */
322
323
  interface ProofEnvelopeInput {
323
324
  /** The proof JWS — the `Authorization: DFOS <token>` token, scheme stripped. */
@@ -346,8 +347,8 @@ interface ProofEnvelopeInput {
346
347
  now?: () => number;
347
348
  }
348
349
  /**
349
- * API-AUTH.md step 4's CONFIG half, hoisted so a caller can run it BEFORE any
350
- * request-dependent gate.
350
+ * INTEGRATIONS.md, Verification algorithm step 4's freshness configuration,
351
+ * hoisted so a caller can run it BEFORE any request-dependent gate.
351
352
  *
352
353
  * ORDER IS LOAD-BEARING. A deployment whose freshness span is out of bounds must
353
354
  * never verify anything, and its misconfiguration must never be REPORTED as a
@@ -399,9 +400,9 @@ interface VerifiedProofEnvelope {
399
400
  */
400
401
  declare const verifyIdentityProofEnvelope: (input: ProofEnvelopeInput, resolvePresenter: ResolveProofPresenter) => Promise<VerifiedProofEnvelope>;
401
402
  /**
402
- * Verify a REQUEST proof's envelope — API-AUTH.md steps 1–7 with the request
403
- * `typ`. The caller then performs steps 8–11 (the credential walk), for which a
404
- * verified proof signature is the gate.
403
+ * Verify a REQUEST proof's envelope — INTEGRATIONS.md, Verification algorithm
404
+ * steps 1–7 with the request `typ`. The caller then performs steps 8–11 (the
405
+ * credential walk), for which a verified proof signature is the gate.
405
406
  */
406
407
  declare const verifyRequestProofEnvelope: (input: ProofEnvelopeInput, resolvePresenter: ResolveProofPresenter) => Promise<VerifiedProofEnvelope>;
407
408
 
@@ -34,9 +34,9 @@ import {
34
34
  verifyDelegationChain,
35
35
  verifyIdentityProofEnvelope,
36
36
  verifyRequestProofEnvelope
37
- } from "../chunk-T2YR5KWQ.js";
38
- import "../chunk-IDVYITX7.js";
39
- import "../chunk-4LG2GEB2.js";
37
+ } from "../chunk-FZHHSC5N.js";
38
+ import "../chunk-F7RHI2CK.js";
39
+ import "../chunk-KAW4MT4P.js";
40
40
  export {
41
41
  ApiRequestVerifyError,
42
42
  Attenuation,
@@ -85,7 +85,7 @@ declare const normalizedId: <T extends string>(prefix: T, id: string) => `${T}_$
85
85
  interface JwsHeader {
86
86
  alg: 'EdDSA';
87
87
  typ: string;
88
- kid: string;
88
+ kid?: string;
89
89
  /** CIDv1 of the operation payload (dag-cbor + SHA-256), signed in the protected header */
90
90
  cid?: string;
91
91
  }
@@ -96,7 +96,9 @@ interface JwsHeader {
96
96
  * signature (64 bytes)
97
97
  */
98
98
  declare const createJws: (options: {
99
- header: JwsHeader;
99
+ header: JwsHeader & {
100
+ kid: string;
101
+ };
100
102
  payload: Record<string, unknown>;
101
103
  sign: (message: Uint8Array) => Promise<Uint8Array>;
102
104
  }) => Promise<string>;
@@ -128,6 +130,28 @@ declare class JwsVerificationError extends Error {
128
130
  constructor(message: string);
129
131
  }
130
132
 
133
+ /**
134
+ * Reject a signed JSON document whose raw text carries something its decoded
135
+ * value can no longer show:
136
+ *
137
+ * 1. DUPLICATE KEYS — PROTOCOL: "A payload containing duplicate keys is
138
+ * malformed: the signature commits to the raw payload bytes while the CID
139
+ * derives from the decoded value." Every JSON parser silently resolves a
140
+ * duplicate (last wins), so the only place to see one is the text.
141
+ * 2. LONE SURROGATE ESCAPES — a `\uD800`-class escape with no pair. TypeScript
142
+ * keeps it; Go's decoder replaces it with U+FFFD. Two verifiers would derive
143
+ * two different CIDs from the same signed bytes, so refusing the escape is
144
+ * the one verdict both can reach.
145
+ *
146
+ * This is a tokenizer, not a parser: it does not validate the grammar — the
147
+ * caller's `JSON.parse` is the grammar judge, and this walk simply stops at the
148
+ * first thing it cannot read. It tracks only enough structure to know which
149
+ * strings are member names of which object.
150
+ *
151
+ * MUST match the Go reference (AssertCanonicalJSONText in json_scan.go).
152
+ */
153
+ declare const assertCanonicalJsonText: (text: string) => void;
154
+
131
155
  /**
132
156
  * Apply the DFOS signature verification profile to a decoded protected header.
133
157
  *
@@ -214,4 +238,4 @@ declare const parseDagCborCID: (cid: string) => CID<unknown, number, number, mul
214
238
  */
215
239
  declare const isCanonicallyEqual: (data1: unknown, data2: unknown) => Promise<boolean>;
216
240
 
217
- export { type JwsHeader, JwsVerificationError, type JwtClaims, type JwtCreateOptions, type JwtHeader, JwtVerificationError, type JwtVerifyOptions, type PrefixedID, assertJwsProfile, base64urlDecode, base64urlEncode, createJws, createJwt, createNewEd25519Keypair, dagCborCanonicalEncode, decodeJwsUnsafe, decodeJwtUnsafe, generateId, generateIdNoPrefix, importEd25519Keypair, isCanonicallyEqual, isValidEd25519Signature, isValidId, normalizedId, parseDagCborCID, sha256, signPayloadEd25519, verifyJws, verifyJwt };
241
+ export { type JwsHeader, JwsVerificationError, type JwtClaims, type JwtCreateOptions, type JwtHeader, JwtVerificationError, type JwtVerifyOptions, type PrefixedID, assertCanonicalJsonText, assertJwsProfile, base64urlDecode, base64urlEncode, createJws, createJwt, createNewEd25519Keypair, dagCborCanonicalEncode, decodeJwsUnsafe, decodeJwtUnsafe, generateId, generateIdNoPrefix, importEd25519Keypair, isCanonicallyEqual, isValidEd25519Signature, isValidId, normalizedId, parseDagCborCID, sha256, signPayloadEd25519, verifyJws, verifyJwt };
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  JwsVerificationError,
3
3
  JwtVerificationError,
4
+ assertCanonicalJsonText,
4
5
  assertJwsProfile,
5
6
  base64urlDecode,
6
7
  base64urlEncode,
@@ -22,10 +23,11 @@ import {
22
23
  signPayloadEd25519,
23
24
  verifyJws,
24
25
  verifyJwt
25
- } from "../chunk-4LG2GEB2.js";
26
+ } from "../chunk-KAW4MT4P.js";
26
27
  export {
27
28
  JwsVerificationError,
28
29
  JwtVerificationError,
30
+ assertCanonicalJsonText,
29
31
  assertJwsProfile,
30
32
  base64urlDecode,
31
33
  base64urlEncode,
@@ -440,7 +440,8 @@ declare const MAX_SIGN_REQUEST_PAYLOAD_SIZE = 4096;
440
440
  *
441
441
  * Unknown envelope fields are preserved-and-ignored, matching every other wire
442
442
  * payload in this file. The signer-side target-payload check is deliberately
443
- * strict instead: a signer refuses fields it cannot render (see SIGNING.md).
443
+ * strict instead: a signer refuses fields it cannot render (see RELAY.md,
444
+ * Signer obligations).
444
445
  */
445
446
  declare const SignRequestPayload: z.ZodObject<{
446
447
  version: z.ZodLiteral<1>;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { JwsHeader, JwsVerificationError, JwtClaims, JwtCreateOptions, JwtHeader, JwtVerificationError, JwtVerifyOptions, PrefixedID, assertJwsProfile, base64urlDecode, base64urlEncode, createJws, createJwt, createNewEd25519Keypair, dagCborCanonicalEncode, decodeJwsUnsafe, decodeJwtUnsafe, generateId, generateIdNoPrefix, importEd25519Keypair, isCanonicallyEqual, isValidEd25519Signature, isValidId, normalizedId, parseDagCborCID, sha256, signPayloadEd25519, verifyJws, verifyJwt } from './crypto/index.js';
2
- export { A as ARTIFACT_CID_ANCHOR_RE, a as ArtifactPayload, b as Attenuation, C as CONTENT_ID_ANCHOR_RE, c as ContentOperation, d as CountersignPayload, e as CredentialVerificationError, f as CreditClaimPayload, D as DFOSCredentialPayload, g as DeclaredKeyState, I as IdentityOperation, h as Iso8601, M as MAX_ARTIFACT_PAYLOAD_SIZE, i as MAX_CREDENTIAL_SIZE, j as MAX_CREDIT_CLAIM_SIZE, k as MAX_KEY_PROOFS, l as MAX_OPERATION_SIZE, m as MAX_SERVICES_ENTRIES, n as MAX_SERVICES_PAYLOAD_SIZE, o as MAX_SIGN_REQUEST_PAYLOAD_SIZE, p as MAX_SIGN_REQUEST_SIZE, q as MultikeyPublicKey, R as ResolvedIdentity, r as RevocationChecker, s as RevocationPayload, S as ServiceEntry, t as ServicesArray, u as SignRequestPayload, v as Signer, V as VerifiedDFOSCredential, w as VerifiedDelegationChain, x as VerifiedIdentity, y as VoidKeyMembership, z as createDFOSCredential, B as decodeDFOSCredentialUnsafe, E as isAttenuated, F as matchesResource, G as parseProtocolTimestampUnix, H as verifyDFOSCredential, J as verifyDelegationChain } from './dfos-credential-CtG0_n4P.js';
3
- export { AnchorKind, CreditClaimFailureReason, CreditClaimVerifyError, CreditEntry, CreditEntryState, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, RECOGNIZED_SERVICE_TYPES, SignRequestFailureReason, SignRequestVerifyError, VerifiedArtifact, VerifiedContentChain, VerifiedCountersignature, VerifiedCreditClaim, VerifiedCreditEntry, VerifiedRevocation, VerifiedSignRequest, anchorsByLabel, assertCanonicalSignRequestPayload, assertServicesWithinCap, buildSignRequest, classifyAnchor, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, isRecognizedServiceType, relayEndpoints, signArtifact, signContentOperation, signCountersignature, signCreditClaim, signIdentityOperation, signRevocation, verifyArtifact, verifyContentChain, verifyContentExtensionFromTrustedState, verifyCountersignature, verifyCreditClaim, verifyCreditEntry, verifyIdentityChain, verifyIdentityExtensionFromTrustedState, verifyRevocation, verifySignRequest } from './chain/index.js';
1
+ export { JwsHeader, JwsVerificationError, JwtClaims, JwtCreateOptions, JwtHeader, JwtVerificationError, JwtVerifyOptions, PrefixedID, assertCanonicalJsonText, assertJwsProfile, base64urlDecode, base64urlEncode, createJws, createJwt, createNewEd25519Keypair, dagCborCanonicalEncode, decodeJwsUnsafe, decodeJwtUnsafe, generateId, generateIdNoPrefix, importEd25519Keypair, isCanonicallyEqual, isValidEd25519Signature, isValidId, normalizedId, parseDagCborCID, sha256, signPayloadEd25519, verifyJws, verifyJwt } from './crypto/index.js';
2
+ export { A as ARTIFACT_CID_ANCHOR_RE, a as ArtifactPayload, b as Attenuation, C as CONTENT_ID_ANCHOR_RE, c as ContentOperation, d as CountersignPayload, e as CredentialVerificationError, f as CreditClaimPayload, D as DFOSCredentialPayload, g as DeclaredKeyState, I as IdentityOperation, h as Iso8601, M as MAX_ARTIFACT_PAYLOAD_SIZE, i as MAX_CREDENTIAL_SIZE, j as MAX_CREDIT_CLAIM_SIZE, k as MAX_KEY_PROOFS, l as MAX_OPERATION_SIZE, m as MAX_SERVICES_ENTRIES, n as MAX_SERVICES_PAYLOAD_SIZE, o as MAX_SIGN_REQUEST_PAYLOAD_SIZE, p as MAX_SIGN_REQUEST_SIZE, q as MultikeyPublicKey, R as ResolvedIdentity, r as RevocationChecker, s as RevocationPayload, S as ServiceEntry, t as ServicesArray, u as SignRequestPayload, v as Signer, V as VerifiedDFOSCredential, w as VerifiedDelegationChain, x as VerifiedIdentity, y as VoidKeyMembership, z as createDFOSCredential, B as decodeDFOSCredentialUnsafe, E as isAttenuated, F as matchesResource, G as parseProtocolTimestampUnix, H as verifyDFOSCredential, J as verifyDelegationChain } from './dfos-credential-j-kHtFvG.js';
3
+ export { AnchorKind, CreditClaimFailureReason, CreditClaimVerifyError, CreditEntry, CreditEntryState, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, RECOGNIZED_SERVICE_TYPES, SignRequestFailureReason, SignRequestVerifyError, VerifiedArtifact, VerifiedContentChain, VerifiedCountersignature, VerifiedCreditClaim, VerifiedCreditEntry, VerifiedRevocation, VerifiedSignRequest, anchorsByLabel, assertCanonicalSignRequestPayload, assertServicesWithinCap, buildSignRequest, classifyAnchor, decodeEd25519PublicMultikey, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, isRecognizedServiceType, relayEndpoints, signArtifact, signContentOperation, signCountersignature, signCreditClaim, signIdentityOperation, signRevocation, verifyArtifact, verifyContentChain, verifyContentExtensionFromTrustedState, verifyCountersignature, verifyCreditClaim, verifyCreditEntry, verifyIdentityChain, verifyIdentityExtensionFromTrustedState, verifyRevocation, verifySignRequest } from './chain/index.js';
4
4
  export { ApiRequestVerifyError, DEFAULT_PROOF_SKEW_SECONDS, DEFAULT_PROOF_WINDOW_SECONDS, DFOS_AUTH_SCHEME, EMPTY_BODY_SHA256, IDENTITY_PROOF_JWS_TYP, IdentityProofPayload, MAX_BODY_BYTES, MAX_PROOF_FRESHNESS_SPAN_SECONDS, MAX_REQUEST_PROOF_SIZE, ParsedProofPayload, ProofEnvelopeInput, ProofExtraMembers, ProofPresenterState, REQUEST_PROOF_JWS_TYP, RequestProofFailurePhase, RequestProofFailureReason, RequestProofPayload, ResolveProofPresenter, SignApiIdentityRequestInput, SignApiRequestInput, VerifiedProofEnvelope, apiIdentitySigningInput, apiRequestSigningInput, assertProofVerifierConfig, buildApiAuthHeaders, buildApiIdentityHeaders, canonicalExtraMembers, invalidProof, misconfiguredProof, parseDfosAuthorization, sha256BodyHash, signApiIdentityRequest, signApiRequest, unverifiableProof, verifyIdentityProofEnvelope, verifyRequestProofEnvelope } from './credentials/index.js';
5
5
  export { DEFAULT_KEY_PROOF_SKEW_SECONDS, KEY_ADD_JWS_TYP, KEY_ROLES, KeyProofFailureReason, KeyProofPayload, KeyProofVerifyError, KeyRole, MAX_KEY_PROOF_SIZE, SignKeyProofInput, VerifiedKeyProof, VerifyChainKeyProofOptions, VerifyKeyProofOptions, isCanonicalRoleSet, keyProofSigningInput, keyWordFingerprint, parseRoleSet, roleSetCovers, serializeRoleSet, signKeyProof, unsafeKeyProofSubject, verifyChainKeyProof, verifyKeyProof } from './key-proof/index.js';
6
6
  export { FoldOperation, INDEX_V1_SCHEMA, IndexDelta, IndexDocument, IndexEntry, LwwDelta, OrderKey, byteCompare, compareHeadPreference, compareLinear, foldIndexV1, foldLwwMap, linearize } from './fold/index.js';
package/dist/index.js CHANGED
@@ -52,7 +52,7 @@ import {
52
52
  verifyIdentityExtensionFromTrustedState,
53
53
  verifyRevocation,
54
54
  verifySignRequest
55
- } from "./chunk-LMAPBTDW.js";
55
+ } from "./chunk-Q7FREWMY.js";
56
56
  import {
57
57
  ApiRequestVerifyError,
58
58
  Attenuation,
@@ -92,7 +92,7 @@ import {
92
92
  verifyDelegationChain,
93
93
  verifyIdentityProofEnvelope,
94
94
  verifyRequestProofEnvelope
95
- } from "./chunk-T2YR5KWQ.js";
95
+ } from "./chunk-FZHHSC5N.js";
96
96
  import {
97
97
  DEFAULT_KEY_PROOF_SKEW_SECONDS,
98
98
  KEY_ADD_JWS_TYP,
@@ -109,16 +109,18 @@ import {
109
109
  unsafeKeyProofSubject,
110
110
  verifyChainKeyProof,
111
111
  verifyKeyProof
112
- } from "./chunk-JVSC67DC.js";
112
+ } from "./chunk-BMIOY5W5.js";
113
113
  import {
114
114
  ED25519_PRIV_MULTICODEC,
115
115
  ED25519_PUB_MULTICODEC,
116
+ decodeEd25519PublicMultikey,
116
117
  decodeMultikey,
117
118
  encodeEd25519Multikey
118
- } from "./chunk-IDVYITX7.js";
119
+ } from "./chunk-F7RHI2CK.js";
119
120
  import {
120
121
  JwsVerificationError,
121
122
  JwtVerificationError,
123
+ assertCanonicalJsonText,
122
124
  assertJwsProfile,
123
125
  base64urlDecode,
124
126
  base64urlEncode,
@@ -140,7 +142,7 @@ import {
140
142
  signPayloadEd25519,
141
143
  verifyJws,
142
144
  verifyJwt
143
- } from "./chunk-4LG2GEB2.js";
145
+ } from "./chunk-KAW4MT4P.js";
144
146
  import {
145
147
  INDEX_V1_SCHEMA,
146
148
  byteCompare,
@@ -205,6 +207,7 @@ export {
205
207
  anchorsByLabel,
206
208
  apiIdentitySigningInput,
207
209
  apiRequestSigningInput,
210
+ assertCanonicalJsonText,
208
211
  assertCanonicalSignRequestPayload,
209
212
  assertJwsProfile,
210
213
  assertProofVerifierConfig,
@@ -226,6 +229,7 @@ export {
226
229
  createNewEd25519Keypair,
227
230
  dagCborCanonicalEncode,
228
231
  decodeDFOSCredentialUnsafe,
232
+ decodeEd25519PublicMultikey,
229
233
  decodeJwsUnsafe,
230
234
  decodeJwtUnsafe,
231
235
  decodeMultikey,
@@ -43,7 +43,7 @@ declare const isCanonicalRoleSet: (value: string) => boolean;
43
43
  declare const roleSetCovers: (value: string, role: KeyRole) => boolean;
44
44
 
45
45
  /**
46
- * The first registered purpose in KEY-PROOF.md's purpose registry: the candidate
46
+ * The registered key-proof purpose in PROTOCOL.md, The envelope: the candidate
47
47
  * key presents for addition to a ceremony-named identity's `authKeys`/
48
48
  * `assertKeys` sets.
49
49
  *
@@ -57,7 +57,7 @@ declare const KEY_ADD_JWS_TYP = "did:dfos:key-add";
57
57
  declare const MAX_KEY_PROOF_SIZE = 4096;
58
58
  /**
59
59
  * RECOMMENDED acceptance window, in seconds, EITHER SIDE of the verifier's clock
60
- * — matching a ceremony's own lifetime (KEY-PROOF.md, Verification step 5).
60
+ * — matching a ceremony's own lifetime (INTEGRATIONS.md, Presentation verification).
61
61
  */
62
62
  declare const DEFAULT_KEY_PROOF_SKEW_SECONDS = 300;
63
63
  /**
@@ -143,11 +143,11 @@ interface SignKeyProofInput {
143
143
  * The protected header is EXACTLY `{"alg":"EdDSA","typ":"<purpose>"}` — two
144
144
  * members, no `kid` (the key is in no chain and rides in the payload) and no
145
145
  * `cid` (there is no operation to bind). It is assembled by hand rather than
146
- * through `createJws`, whose `JwsHeader` requires a `kid` this envelope must not
146
+ * through `createJws`, whose header input requires a `kid` this envelope must not
147
147
  * carry.
148
148
  *
149
- * HOLDER OBLIGATIONS THIS FUNCTION CANNOT DISCHARGE (KEY-PROOF.md, Holder
150
- * Obligations). A holder MUST show its human — before calling this — the
149
+ * HOLDER OBLIGATIONS THIS FUNCTION CANNOT DISCHARGE (INTEGRATIONS.md, Holder
150
+ * obligations). A holder MUST show its human — before calling this — the
151
151
  * audience, the purpose, the adopting identity, and the roles. A proof is
152
152
  * consent, and consent that was never displayed was never given.
153
153
  *
@@ -210,10 +210,10 @@ interface VerifyKeyProofOptions {
210
210
  /** What a verified key proof hands back. */
211
211
  interface VerifiedKeyProof {
212
212
  /**
213
- * The validated payload. THE CALLER MUST NOW RUN STEP 6 against `payload.nonce`:
214
- * check that it is a nonce this verifier minted, for this ceremony, not yet
215
- * consumed, and consume it ATOMICALLY (check-and-delete) so two racing
216
- * completions cannot both pass.
213
+ * The validated payload. THE CALLER MUST NOW RUN THE NONCE CHECK against
214
+ * `payload.nonce`: check that it is a nonce this verifier minted, for this
215
+ * ceremony, not yet consumed, and consume it ATOMICALLY (check-and-delete) so
216
+ * two racing completions cannot both pass.
217
217
  */
218
218
  payload: KeyProofPayload;
219
219
  /** The header `typ` — equal to `expectedTyp`, since anything else rejected. */
@@ -222,17 +222,18 @@ interface VerifiedKeyProof {
222
222
  now: number;
223
223
  }
224
224
  /**
225
- * Verify a key proof AT PRESENTATION TIME — KEY-PROOF.md's verification algorithm
226
- * steps 1–5 and 7: size cap, header gates, the closed payload schema over
227
- * CANONICAL bytes, the four expectation arms (audience, did, roleSet, prevCID),
228
- * freshness, and the signature against the payload's OWN `publicKeyMultibase`.
225
+ * Verify a key proof AT PRESENTATION TIME — INTEGRATIONS.md, Presentation
226
+ * verification except its nonce check: size cap, header gates, the closed
227
+ * payload schema over CANONICAL bytes, the four expectation arms (audience,
228
+ * did, roleSet, prevCID), freshness, and the signature against the payload's
229
+ * OWN `publicKeyMultibase`.
229
230
  *
230
231
  * EVERY EXPECTATION IS THE DEPLOYMENT'S OWN VALUE. There is no arm here that
231
232
  * compares the envelope to itself. `expectedDid`, `expectedRoleSet` and
232
233
  * `expectedPrevCID` are the position the completing authority is about to WRITE;
233
234
  * if the envelope names a different one, the holder consented to something else.
234
235
  *
235
- * STEP 6 (NONCE) IS THE CALLER'S, and this function cannot stand in for it. The
236
+ * THE NONCE CHECK IS THE CALLER'S, and this function cannot stand in for it. The
236
237
  * nonce MUST be one this verifier minted, for this ceremony, not yet consumed,
237
238
  * checked and consumed ATOMICALLY — a check-and-delete against the verifier's
238
239
  * own store, which is state this pure function does not hold. It is returned on
@@ -14,9 +14,9 @@ import {
14
14
  unsafeKeyProofSubject,
15
15
  verifyChainKeyProof,
16
16
  verifyKeyProof
17
- } from "../chunk-JVSC67DC.js";
18
- import "../chunk-IDVYITX7.js";
19
- import "../chunk-4LG2GEB2.js";
17
+ } from "../chunk-BMIOY5W5.js";
18
+ import "../chunk-F7RHI2CK.js";
19
+ import "../chunk-KAW4MT4P.js";
20
20
  export {
21
21
  DEFAULT_KEY_PROOF_SKEW_SECONDS,
22
22
  KEY_ADD_JWS_TYP,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metalabel/dfos-protocol",
3
- "version": "0.51.0",
3
+ "version": "0.52.1",
4
4
  "type": "module",
5
5
  "description": "DFOS Protocol — Ed25519 signed chain primitives, services, credentials, and verification",
6
6
  "license": "MIT",