@metalabel/dfos-protocol 0.36.1 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -49,27 +49,48 @@ import {
49
49
  verifyIdentityExtensionFromTrustedState,
50
50
  verifyRevocation,
51
51
  verifySignRequest
52
- } from "./chunk-W7IFQNUH.js";
52
+ } from "./chunk-SGPSXM56.js";
53
53
  import {
54
+ ApiRequestVerifyError,
54
55
  Attenuation,
55
- AuthTokenClaims,
56
- AuthTokenVerificationError,
57
56
  CredentialVerificationError,
57
+ DEFAULT_PROOF_SKEW_SECONDS,
58
+ DEFAULT_PROOF_WINDOW_SECONDS,
58
59
  DFOSCredentialPayload,
60
+ DFOS_AUTH_SCHEME,
59
61
  ED25519_PRIV_MULTICODEC,
60
62
  ED25519_PUB_MULTICODEC,
63
+ EMPTY_BODY_SHA256,
64
+ IDENTITY_PROOF_JWS_TYP,
65
+ MAX_BODY_BYTES,
61
66
  MAX_CREDENTIAL_SIZE,
62
- createAuthToken,
67
+ MAX_PROOF_FRESHNESS_SPAN_SECONDS,
68
+ MAX_REQUEST_PROOF_SIZE,
69
+ REQUEST_PROOF_JWS_TYP,
70
+ apiIdentitySigningInput,
71
+ apiRequestSigningInput,
72
+ assertProofVerifierConfig,
73
+ buildApiAuthHeaders,
74
+ buildApiIdentityHeaders,
75
+ canonicalExtraMembers,
63
76
  createDFOSCredential,
64
77
  decodeDFOSCredentialUnsafe,
65
78
  decodeMultikey,
66
79
  encodeEd25519Multikey,
80
+ invalidProof,
67
81
  isAttenuated,
68
82
  matchesResource,
69
- verifyAuthToken,
83
+ misconfiguredProof,
84
+ parseDfosAuthorization,
85
+ sha256BodyHash,
86
+ signApiIdentityRequest,
87
+ signApiRequest,
88
+ unverifiableProof,
70
89
  verifyDFOSCredential,
71
- verifyDelegationChain
72
- } from "./chunk-XP644GQK.js";
90
+ verifyDelegationChain,
91
+ verifyIdentityProofEnvelope,
92
+ verifyRequestProofEnvelope
93
+ } from "./chunk-NXQW6EBF.js";
73
94
  import {
74
95
  JwsVerificationError,
75
96
  JwtVerificationError,
@@ -106,34 +127,42 @@ import {
106
127
  } from "./chunk-XF7T45EQ.js";
107
128
  export {
108
129
  ARTIFACT_CID_ANCHOR_RE,
130
+ ApiRequestVerifyError,
109
131
  ArtifactPayload,
110
132
  Attenuation,
111
- AuthTokenClaims,
112
- AuthTokenVerificationError,
113
133
  CONTENT_ID_ANCHOR_RE,
114
134
  ContentOperation,
115
135
  CountersignPayload,
116
136
  CredentialVerificationError,
117
137
  CreditClaimPayload,
118
138
  CreditClaimVerifyError,
139
+ DEFAULT_PROOF_SKEW_SECONDS,
140
+ DEFAULT_PROOF_WINDOW_SECONDS,
119
141
  DFOSCredentialPayload,
142
+ DFOS_AUTH_SCHEME,
120
143
  ED25519_PRIV_MULTICODEC,
121
144
  ED25519_PUB_MULTICODEC,
145
+ EMPTY_BODY_SHA256,
146
+ IDENTITY_PROOF_JWS_TYP,
122
147
  INDEX_V1_SCHEMA,
123
148
  IdentityOperation,
124
149
  Iso8601,
125
150
  JwsVerificationError,
126
151
  JwtVerificationError,
127
152
  MAX_ARTIFACT_PAYLOAD_SIZE,
153
+ MAX_BODY_BYTES,
128
154
  MAX_CREDENTIAL_SIZE,
129
155
  MAX_CREDIT_CLAIM_SIZE,
130
156
  MAX_OPERATION_SIZE,
157
+ MAX_PROOF_FRESHNESS_SPAN_SECONDS,
158
+ MAX_REQUEST_PROOF_SIZE,
131
159
  MAX_SERVICES_ENTRIES,
132
160
  MAX_SERVICES_PAYLOAD_SIZE,
133
161
  MAX_SIGN_REQUEST_PAYLOAD_SIZE,
134
162
  MAX_SIGN_REQUEST_SIZE,
135
163
  MultikeyPublicKey,
136
164
  RECOGNIZED_SERVICE_TYPES,
165
+ REQUEST_PROOF_JWS_TYP,
137
166
  RevocationPayload,
138
167
  ServiceEntry,
139
168
  ServicesArray,
@@ -141,17 +170,22 @@ export {
141
170
  SignRequestVerifyError,
142
171
  VerifiedIdentity,
143
172
  anchorsByLabel,
173
+ apiIdentitySigningInput,
174
+ apiRequestSigningInput,
144
175
  assertCanonicalSignRequestPayload,
145
176
  assertJwsProfile,
177
+ assertProofVerifierConfig,
146
178
  assertServicesWithinCap,
147
179
  base64urlDecode,
148
180
  base64urlEncode,
181
+ buildApiAuthHeaders,
182
+ buildApiIdentityHeaders,
149
183
  buildSignRequest,
150
184
  byteCompare,
185
+ canonicalExtraMembers,
151
186
  classifyAnchor,
152
187
  compareHeadPreference,
153
188
  compareLinear,
154
- createAuthToken,
155
189
  createDFOSCredential,
156
190
  createJws,
157
191
  createJwt,
@@ -169,6 +203,7 @@ export {
169
203
  generateId,
170
204
  generateIdNoPrefix,
171
205
  importEd25519Keypair,
206
+ invalidProof,
172
207
  isAttenuated,
173
208
  isCanonicallyEqual,
174
209
  isRecognizedServiceType,
@@ -176,11 +211,16 @@ export {
176
211
  isValidId,
177
212
  linearize,
178
213
  matchesResource,
214
+ misconfiguredProof,
179
215
  normalizedId,
180
216
  parseDagCborCID,
217
+ parseDfosAuthorization,
181
218
  parseProtocolTimestampUnix,
182
219
  relayEndpoints,
183
220
  sha256,
221
+ sha256BodyHash,
222
+ signApiIdentityRequest,
223
+ signApiRequest,
184
224
  signArtifact,
185
225
  signContentOperation,
186
226
  signCountersignature,
@@ -188,8 +228,8 @@ export {
188
228
  signIdentityOperation,
189
229
  signPayloadEd25519,
190
230
  signRevocation,
231
+ unverifiableProof,
191
232
  verifyArtifact,
192
- verifyAuthToken,
193
233
  verifyContentChain,
194
234
  verifyContentExtensionFromTrustedState,
195
235
  verifyCountersignature,
@@ -199,8 +239,10 @@ export {
199
239
  verifyDelegationChain,
200
240
  verifyIdentityChain,
201
241
  verifyIdentityExtensionFromTrustedState,
242
+ verifyIdentityProofEnvelope,
202
243
  verifyJws,
203
244
  verifyJwt,
245
+ verifyRequestProofEnvelope,
204
246
  verifyRevocation,
205
247
  verifySignRequest
206
248
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metalabel/dfos-protocol",
3
- "version": "0.36.1",
3
+ "version": "0.38.0",
4
4
  "type": "module",
5
5
  "description": "DFOS Protocol — Ed25519 signed chain primitives, services, credentials, and verification",
6
6
  "license": "MIT",
@@ -1,394 +0,0 @@
1
- import {
2
- createJws,
3
- createJwt,
4
- dagCborCanonicalEncode,
5
- decodeJwsUnsafe,
6
- verifyJws,
7
- verifyJwt
8
- } from "./chunk-4LG2GEB2.js";
9
-
10
- // src/credentials/schemas.ts
11
- import { z } from "zod";
12
- var MAX_ATT = 32;
13
- var MAX_PRF = 1;
14
- var MAX_CREDENTIAL_SIZE = 262144;
15
- var Attenuation = z.looseObject({
16
- resource: z.string().min(1),
17
- action: z.string().min(1)
18
- });
19
- var DFOSCredentialPayload = z.looseObject({
20
- version: z.literal(1),
21
- type: z.literal("DFOSCredential"),
22
- /** Issuer DID */
23
- iss: z.string().min(1),
24
- /** Audience DID or "*" for public credentials */
25
- aud: z.string().min(1),
26
- /** Attenuations — resource + action pairs */
27
- att: z.array(Attenuation).min(1).max(MAX_ATT),
28
- /** Parent credential JWS tokens (for delegation chains) */
29
- prf: z.array(z.string()).max(MAX_PRF).default([]),
30
- /** Expiration — unix seconds */
31
- exp: z.number().int().positive(),
32
- /** Issued at — unix seconds */
33
- iat: z.number().int().positive()
34
- });
35
- var AuthTokenClaims = z.looseObject({
36
- /** Issuer — the DID proving identity */
37
- iss: z.string(),
38
- /** Subject — same as iss for auth tokens */
39
- sub: z.string(),
40
- /** Audience — target relay hostname (prevents cross-relay replay) */
41
- aud: z.string(),
42
- /** Expiration — unix seconds, short-lived (minutes) */
43
- exp: z.number().int().positive(),
44
- /** Issued at — unix seconds */
45
- iat: z.number().int().positive()
46
- });
47
-
48
- // src/credentials/auth-token.ts
49
- var createAuthToken = async (options) => {
50
- if (!options.kid.includes("#")) {
51
- throw new Error("kid must be a DID URL (did:dfos:xxx#key_yyy)");
52
- }
53
- const kidDid = options.kid.substring(0, options.kid.indexOf("#"));
54
- if (kidDid !== options.iss) {
55
- throw new Error("kid DID does not match iss");
56
- }
57
- const now = options.iat ?? Math.floor(Date.now() / 1e3);
58
- const header = { alg: "EdDSA", typ: "JWT", kid: options.kid };
59
- const payload = {
60
- iss: options.iss,
61
- sub: options.iss,
62
- aud: options.aud,
63
- exp: options.exp,
64
- iat: now
65
- };
66
- return createJwt({ header, payload, sign: options.sign });
67
- };
68
- var verifyAuthToken = (options) => {
69
- const { header, payload } = verifyJwt({
70
- token: options.token,
71
- publicKey: options.publicKey,
72
- audience: options.audience,
73
- ...options.currentTime !== void 0 ? { currentTime: options.currentTime } : {}
74
- });
75
- const result = AuthTokenClaims.safeParse(payload);
76
- if (!result.success) {
77
- const messages = result.error.issues.map((e) => e.message).join(", ");
78
- throw new AuthTokenVerificationError(`invalid auth token claims: ${messages}`);
79
- }
80
- const currentTime = options.currentTime ?? Math.floor(Date.now() / 1e3);
81
- if (result.data.iat > currentTime) {
82
- throw new AuthTokenVerificationError("auth token not yet valid (iat is in the future)");
83
- }
84
- const kid = header.kid;
85
- if (!kid || !kid.includes("#")) {
86
- throw new AuthTokenVerificationError("auth token kid must be a DID URL");
87
- }
88
- const kidDid = kid.substring(0, kid.indexOf("#"));
89
- if (kidDid !== result.data.iss) {
90
- throw new AuthTokenVerificationError("auth token kid DID does not match iss");
91
- }
92
- return {
93
- iss: result.data.iss,
94
- aud: result.data.aud,
95
- exp: result.data.exp,
96
- iat: result.data.iat,
97
- kid
98
- };
99
- };
100
- var AuthTokenVerificationError = class extends Error {
101
- constructor(message) {
102
- super(message);
103
- this.name = "AuthTokenVerificationError";
104
- }
105
- };
106
-
107
- // src/chain/multikey.ts
108
- import { base58btc } from "multiformats/bases/base58";
109
- var ED25519_PUB_PREFIX = new Uint8Array([237, 1]);
110
- var ED25519_PRIV_PREFIX = new Uint8Array([128, 38]);
111
- var ED25519_PUB_MULTICODEC = 237;
112
- var ED25519_PRIV_MULTICODEC = 4864;
113
- var encodeEd25519Multikey = (publicKeyBytes) => {
114
- if (publicKeyBytes.length !== 32) {
115
- throw new Error(`expected 32-byte Ed25519 public key, got ${publicKeyBytes.length}`);
116
- }
117
- const prefixed = new Uint8Array(ED25519_PUB_PREFIX.length + publicKeyBytes.length);
118
- prefixed.set(ED25519_PUB_PREFIX);
119
- prefixed.set(publicKeyBytes, ED25519_PUB_PREFIX.length);
120
- return base58btc.encode(prefixed);
121
- };
122
- var decodeMultikey = (multibase) => {
123
- const bytes = base58btc.decode(multibase);
124
- if (bytes.length < 2) {
125
- throw new Error("multikey too short");
126
- }
127
- if (bytes[0] === ED25519_PUB_PREFIX[0] && bytes[1] === ED25519_PUB_PREFIX[1]) {
128
- const keyBytes = bytes.slice(2);
129
- if (keyBytes.length !== 32) {
130
- throw new Error(`expected 32-byte Ed25519 public key, got ${keyBytes.length}`);
131
- }
132
- return { keyBytes, codec: ED25519_PUB_MULTICODEC };
133
- }
134
- if (bytes[0] === ED25519_PRIV_PREFIX[0] && bytes[1] === ED25519_PRIV_PREFIX[1]) {
135
- const keyBytes = bytes.slice(2);
136
- if (keyBytes.length !== 32) {
137
- throw new Error(`expected 32-byte Ed25519 private key, got ${keyBytes.length}`);
138
- }
139
- return { keyBytes, codec: ED25519_PRIV_MULTICODEC };
140
- }
141
- throw new Error(
142
- `unsupported multikey codec: [0x${bytes[0]?.toString(16)}, 0x${bytes[1]?.toString(16)}]`
143
- );
144
- };
145
-
146
- // src/credentials/dfos-credential.ts
147
- var resolveKeyFromIdentity = (identity, kid) => {
148
- const hashIdx = kid.indexOf("#");
149
- if (hashIdx < 0) throw new CredentialVerificationError("kid must be a DID URL");
150
- const keyId = kid.substring(hashIdx + 1);
151
- const allKeys = [...identity.authKeys, ...identity.assertKeys, ...identity.controllerKeys];
152
- const key = allKeys.find((k) => k.id === keyId);
153
- if (!key) {
154
- throw new CredentialVerificationError(`key ${keyId} not found on identity ${identity.did}`);
155
- }
156
- const { keyBytes } = decodeMultikey(key.publicKeyMultibase);
157
- return keyBytes;
158
- };
159
- var createDFOSCredential = async (options) => {
160
- const kid = `${options.issuerDID}#${options.keyId}`;
161
- const now = options.iat ?? Math.floor(Date.now() / 1e3);
162
- const payload = {
163
- version: 1,
164
- type: "DFOSCredential",
165
- iss: options.issuerDID,
166
- aud: options.audienceDID,
167
- att: options.att,
168
- prf: options.prf ?? [],
169
- exp: options.exp,
170
- iat: now
171
- };
172
- const parseResult = DFOSCredentialPayload.safeParse(payload);
173
- if (!parseResult.success) {
174
- const messages = parseResult.error.issues.map((e) => e.message).join(", ");
175
- throw new Error(`invalid credential payload: ${messages}`);
176
- }
177
- const encoded = await dagCborCanonicalEncode(payload);
178
- const credentialCID = encoded.cid.toString();
179
- const jwsToken = await createJws({
180
- header: { alg: "EdDSA", typ: "did:dfos:credential", kid, cid: credentialCID },
181
- payload,
182
- sign: options.signer
183
- });
184
- return jwsToken;
185
- };
186
- var verifyDFOSCredential = async (jwsToken, options) => {
187
- if (jwsToken.length > MAX_CREDENTIAL_SIZE) {
188
- throw new CredentialVerificationError(
189
- `credential exceeds max size: ${jwsToken.length} > ${MAX_CREDENTIAL_SIZE}`
190
- );
191
- }
192
- const decoded = decodeJwsUnsafe(jwsToken);
193
- if (!decoded) throw new CredentialVerificationError("failed to decode credential JWS");
194
- if (decoded.header.typ !== "did:dfos:credential") {
195
- throw new CredentialVerificationError(`invalid typ: ${decoded.header.typ}`);
196
- }
197
- const result = DFOSCredentialPayload.safeParse(decoded.payload);
198
- if (!result.success) {
199
- const messages = result.error.issues.map((e) => e.message).join(", ");
200
- throw new CredentialVerificationError(`invalid credential payload: ${messages}`);
201
- }
202
- const payload = result.data;
203
- const kid = decoded.header.kid;
204
- const hashIdx = kid.indexOf("#");
205
- if (hashIdx < 0) throw new CredentialVerificationError("credential kid must be a DID URL");
206
- const kidDid = kid.substring(0, hashIdx);
207
- if (kidDid !== payload.iss) {
208
- throw new CredentialVerificationError("credential kid DID does not match iss");
209
- }
210
- const identity = await options.resolveIdentity(payload.iss);
211
- if (!identity) {
212
- throw new CredentialVerificationError(`issuer identity not found: ${payload.iss}`);
213
- }
214
- if (identity.isDeleted) {
215
- throw new CredentialVerificationError(`issuer identity is deleted: ${payload.iss}`);
216
- }
217
- const publicKey = resolveKeyFromIdentity(identity, kid);
218
- try {
219
- verifyJws({ token: jwsToken, publicKey });
220
- } catch {
221
- throw new CredentialVerificationError("invalid credential signature");
222
- }
223
- const encoded = await dagCborCanonicalEncode(payload);
224
- const credentialCID = encoded.cid.toString();
225
- if (!decoded.header.cid) {
226
- throw new CredentialVerificationError("missing cid in credential header");
227
- }
228
- if (decoded.header.cid !== credentialCID) {
229
- throw new CredentialVerificationError("credential cid mismatch");
230
- }
231
- const now = options.now ?? Math.floor(Date.now() / 1e3);
232
- if (payload.iat > now) {
233
- throw new CredentialVerificationError("credential not yet valid (iat is in the future)");
234
- }
235
- if (payload.exp <= now) {
236
- throw new CredentialVerificationError("credential expired");
237
- }
238
- return {
239
- iss: payload.iss,
240
- aud: payload.aud,
241
- att: payload.att,
242
- prf: payload.prf,
243
- exp: payload.exp,
244
- iat: payload.iat,
245
- credentialCID,
246
- signerKeyId: kid
247
- };
248
- };
249
- var verifyDelegationChain = async (credential, options) => {
250
- const chain = [credential];
251
- let current = credential;
252
- const maxDepth = 16;
253
- for (let depth = 0; depth < maxDepth; depth++) {
254
- if (current.prf.length === 0) {
255
- if (current.iss !== options.rootDID) {
256
- throw new CredentialVerificationError(
257
- `delegation chain root issuer ${current.iss} does not match expected root ${options.rootDID}`
258
- );
259
- }
260
- return { credential, chain, rootDID: options.rootDID };
261
- }
262
- if (current.prf.length > 1) {
263
- throw new CredentialVerificationError(
264
- "delegation chain: multi-parent credentials are not supported (prf must have at most one entry)"
265
- );
266
- }
267
- const parent = await verifyDFOSCredential(current.prf[0], {
268
- resolveIdentity: options.resolveIdentity,
269
- ...options.now !== void 0 ? { now: options.now } : {}
270
- });
271
- if (options.isRevoked) {
272
- const revoked = await options.isRevoked(parent.iss, parent.credentialCID, options.asOfUnix);
273
- if (revoked) {
274
- throw new CredentialVerificationError("parent credential in delegation chain is revoked");
275
- }
276
- }
277
- if (parent.aud !== "*" && parent.aud !== current.iss) {
278
- throw new CredentialVerificationError(
279
- `delegation gap: parent credential audience ${parent.aud} does not match child issuer ${current.iss}`
280
- );
281
- }
282
- if (current.exp > parent.exp) {
283
- throw new CredentialVerificationError(
284
- "delegation chain: child credential expiry exceeds parent expiry"
285
- );
286
- }
287
- if (!isAttenuated(parent.att, current.att)) {
288
- throw new CredentialVerificationError(
289
- "delegation chain: child credential scope exceeds parent scope"
290
- );
291
- }
292
- chain.push(parent);
293
- current = parent;
294
- }
295
- throw new CredentialVerificationError("delegation chain too deep (max 16 credentials)");
296
- };
297
- var parseResource = (resource) => {
298
- const colonIdx = resource.indexOf(":");
299
- if (colonIdx < 0) return null;
300
- return { type: resource.substring(0, colonIdx), id: resource.substring(colonIdx + 1) };
301
- };
302
- var parseActions = (action) => new Set(
303
- action.split(",").map((a) => a.trim()).filter((a) => a !== "")
304
- );
305
- var isAttenuated = (parentAtt, childAtt) => {
306
- return childAtt.every((childEntry) => {
307
- const childRes = parseResource(childEntry.resource);
308
- if (!childRes) return false;
309
- const childActions = parseActions(childEntry.action);
310
- return parentAtt.some((parentEntry) => {
311
- const parentRes = parseResource(parentEntry.resource);
312
- if (!parentRes) return false;
313
- const parentActions = parseActions(parentEntry.action);
314
- for (const a of childActions) {
315
- if (!parentActions.has(a)) return false;
316
- }
317
- if (parentRes.type === "chain" && parentRes.id === "*") {
318
- return childRes.type === "chain";
319
- }
320
- if (childRes.type === "chain" && childRes.id === "*") {
321
- return false;
322
- }
323
- if (childRes.type === "chain" && parentRes.type === "chain") {
324
- return childRes.id === parentRes.id;
325
- }
326
- if (childRes.type !== "chain" && parentRes.type !== "chain") {
327
- return childEntry.resource === parentEntry.resource;
328
- }
329
- return false;
330
- });
331
- });
332
- };
333
- var matchesResource = async (att, resource, action) => {
334
- const requestedRes = parseResource(resource);
335
- if (!requestedRes) return false;
336
- const requestedActions = parseActions(action);
337
- for (const entry of att) {
338
- const entryRes = parseResource(entry.resource);
339
- if (!entryRes) continue;
340
- const entryActions = parseActions(entry.action);
341
- let actionsCovered = true;
342
- for (const a of requestedActions) {
343
- if (!entryActions.has(a)) {
344
- actionsCovered = false;
345
- break;
346
- }
347
- }
348
- if (!actionsCovered) continue;
349
- if (entryRes.type === "chain" && entryRes.id === "*" && requestedRes.type === "chain") {
350
- return true;
351
- }
352
- if (entryRes.type === requestedRes.type && entryRes.id === requestedRes.id) {
353
- return true;
354
- }
355
- }
356
- return false;
357
- };
358
- var decodeDFOSCredentialUnsafe = (jwsToken) => {
359
- const decoded = decodeJwsUnsafe(jwsToken);
360
- if (!decoded) return null;
361
- const result = DFOSCredentialPayload.safeParse(decoded.payload);
362
- if (!result.success) return null;
363
- return {
364
- header: decoded.header,
365
- payload: result.data
366
- };
367
- };
368
- var CredentialVerificationError = class extends Error {
369
- constructor(message) {
370
- super(message);
371
- this.name = "CredentialVerificationError";
372
- }
373
- };
374
-
375
- export {
376
- ED25519_PUB_MULTICODEC,
377
- ED25519_PRIV_MULTICODEC,
378
- encodeEd25519Multikey,
379
- decodeMultikey,
380
- MAX_CREDENTIAL_SIZE,
381
- Attenuation,
382
- DFOSCredentialPayload,
383
- AuthTokenClaims,
384
- createAuthToken,
385
- verifyAuthToken,
386
- AuthTokenVerificationError,
387
- createDFOSCredential,
388
- verifyDFOSCredential,
389
- verifyDelegationChain,
390
- isAttenuated,
391
- matchesResource,
392
- decodeDFOSCredentialUnsafe,
393
- CredentialVerificationError
394
- };