@synoi/sraid 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +51 -6
  2. package/dist/attestation.d.ts +8 -49
  3. package/dist/attestation.d.ts.map +1 -1
  4. package/dist/attestation.js +15 -46
  5. package/dist/attestation.js.map +1 -1
  6. package/dist/internal/attestation-core.d.ts +85 -0
  7. package/dist/internal/attestation-core.d.ts.map +1 -0
  8. package/dist/internal/attestation-core.js +78 -0
  9. package/dist/internal/attestation-core.js.map +1 -0
  10. package/dist/internal/base64-browser.d.ts +31 -0
  11. package/dist/internal/base64-browser.d.ts.map +1 -0
  12. package/dist/internal/base64-browser.js +39 -0
  13. package/dist/internal/base64-browser.js.map +1 -0
  14. package/dist/internal/base64-validate.d.ts +19 -0
  15. package/dist/internal/base64-validate.d.ts.map +1 -0
  16. package/dist/internal/base64-validate.js +33 -0
  17. package/dist/internal/base64-validate.js.map +1 -0
  18. package/dist/internal/base64.d.ts +7 -5
  19. package/dist/internal/base64.d.ts.map +1 -1
  20. package/dist/internal/base64.js +7 -19
  21. package/dist/internal/base64.js.map +1 -1
  22. package/dist/internal/content-core.d.ts +68 -0
  23. package/dist/internal/content-core.d.ts.map +1 -0
  24. package/dist/internal/content-core.js +87 -0
  25. package/dist/internal/content-core.js.map +1 -0
  26. package/dist/internal/ed25519-browser.d.ts +33 -0
  27. package/dist/internal/ed25519-browser.d.ts.map +1 -0
  28. package/dist/internal/ed25519-browser.js +79 -0
  29. package/dist/internal/ed25519-browser.js.map +1 -0
  30. package/dist/internal/mldsa-browser.d.ts +27 -0
  31. package/dist/internal/mldsa-browser.d.ts.map +1 -0
  32. package/dist/internal/mldsa-browser.js +35 -0
  33. package/dist/internal/mldsa-browser.js.map +1 -0
  34. package/dist/internal/sha256-browser.d.ts +17 -0
  35. package/dist/internal/sha256-browser.d.ts.map +1 -0
  36. package/dist/internal/sha256-browser.js +27 -0
  37. package/dist/internal/sha256-browser.js.map +1 -0
  38. package/dist/oid.d.ts +2 -52
  39. package/dist/oid.d.ts.map +1 -1
  40. package/dist/oid.js +9 -71
  41. package/dist/oid.js.map +1 -1
  42. package/dist/verify-browser.d.ts +80 -0
  43. package/dist/verify-browser.d.ts.map +1 -0
  44. package/dist/verify-browser.js +157 -0
  45. package/dist/verify-browser.js.map +1 -0
  46. package/package.json +21 -2
  47. package/src/attestation.ts +29 -88
  48. package/src/internal/attestation-core.ts +122 -0
  49. package/src/internal/base64-browser.ts +39 -0
  50. package/src/internal/base64-validate.ts +31 -0
  51. package/src/internal/base64.ts +7 -16
  52. package/src/internal/content-core.ts +88 -0
  53. package/src/internal/ed25519-browser.ts +85 -0
  54. package/src/internal/mldsa-browser.ts +39 -0
  55. package/src/internal/sha256-browser.ts +28 -0
  56. package/src/oid.ts +10 -73
  57. package/src/verify-browser.ts +185 -0
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @synoi/sraid
2
2
 
3
3
  Reference TypeScript implementation of **SRAID (Self-Routing Addressable
4
- Identity Data)** L0 of the SynOI SRAID Stack. MIT-licensed.
4
+ Identity Data)**, L0 of the SynOI SRAID Stack. MIT-licensed.
5
5
 
6
6
  SRAID defines:
7
7
 
@@ -12,8 +12,8 @@ SRAID defines:
12
12
  - an **L3 Merkle-DAG lineage** layer (`prev` edge + typed `links[]`), hashed into the OID so a head id commits its whole reachable history, and
13
13
  - a supersession record, the **SRO** (Supersession Record Object), that links a successor CDRO to its predecessor.
14
14
 
15
- The three legacy supersession mechanisms the self-asserted `supersedes`
16
- string, the standalone SRO, and the `prev` edge are unified by the lineage
15
+ The three legacy supersession mechanisms, the self-asserted `supersedes`
16
+ string, the standalone SRO, and the `prev` edge, are unified by the lineage
17
17
  helpers (`lineageLinks`, `supersededOids`, `latestWins`). `latestWins` resolves
18
18
  a set of versions to its single head by following the identity-bound `prev`/
19
19
  `links` edges, giving a verifier a **latest-wins / monotone** rule instead of an
@@ -24,8 +24,8 @@ transparency-log property (DESIGN, not yet deployed). The `set_complete` field
24
24
  on `LatestWinsResult` is the local signal that lets a caller detect an
25
25
  incomplete (potentially withheld) set.
26
26
 
27
- Higher layers Vault/Resolver (L1), Inference Broker/Resonance (L2), GAP (L3)
28
- are built on SRAID objects. This package is intentionally tiny and has no
27
+ Higher layers are built on SRAID objects: Vault/Resolver at L1, Inference
28
+ Broker/Resonance at L2, GAP at L3. This package is intentionally tiny and has no
29
29
  storage, no HTTP surface, and no governance logic.
30
30
 
31
31
  ## Install
@@ -113,6 +113,51 @@ interface SRO { /* supersession record */ }
113
113
  interface SignatureEnvelope { ed25519: string; ml_dsa_65: string; signer_kid: string }
114
114
  ```
115
115
 
116
+ ## Browser, Chrome extension, service worker
117
+
118
+ The default (`.`) entry statically imports `node:crypto` in three places
119
+ (`ed25519.ts`, `mldsa.ts`, `oid.ts`), so `import '@synoi/sraid'` breaks any
120
+ browser bundle. Import the `./verify-browser` subpath instead. It carries exactly
121
+ what a v2 hybrid DSSE receipt verifier needs, with no static `node:crypto`
122
+ anywhere in its graph.
123
+
124
+ ```ts
125
+ import { verifyAttestation, canonicalize, cdroContentCore } from '@synoi/sraid/verify-browser'
126
+
127
+ const v = await verifyAttestation({
128
+ envelope: receipt.attestation,
129
+ ed25519_pub, // raw 32 bytes
130
+ ml_dsa_pub, // raw 1952 bytes
131
+ expectedPayloadType: 'application/vnd.synoi.gap+json',
132
+ })
133
+ // { valid: true, reasons: [] } only when BOTH signatures verify over the PAE
134
+ ```
135
+
136
+ ```ts
137
+ // The subpath surface. Four of these are ASYNC where the node entry is sync,
138
+ // because WebCrypto is Promise-based. Everything else is shared byte-for-byte
139
+ // with the node entry, so the two agree on the same input.
140
+ verifyAttestation(input): Promise<{ valid: boolean; reasons: string[] }> // ASYNC
141
+ cdroOid(cdro: unknown): Promise<string> // ASYNC
142
+ oidOf(canonical: unknown): Promise<string> // ASYNC
143
+ oidOfCanonical(canonical: string | Uint8Array): Promise<string> // ASYNC
144
+
145
+ canonicalize(value: unknown): string // pure, identical to the node entry
146
+ cdroContentCore(cdro: unknown): Record<string, unknown>
147
+ pae(payloadType: string, payload: string | Uint8Array): Uint8Array
148
+ CDRO_ENVELOPE_FIELDS: readonly string[]
149
+ ALG_ED25519, ALG_ML_DSA_65
150
+ ```
151
+
152
+ Crypto backends, since browsers have no native ML-DSA and no `node:crypto`:
153
+ Ed25519 verify on WebCrypto (RFC 8032 cofactored, matching the node path) with a
154
+ `@noble/curves` fallback below the WebCrypto-Ed25519 support floor; ML-DSA-65
155
+ verify on `@noble/post-quantum`; SHA-256 on WebCrypto `subtle.digest`.
156
+
157
+ The node default entry is unchanged and keeps its synchronous `node:crypto` fast
158
+ paths. This subpath is purely additive. Added in 0.3.0; earlier versions export
159
+ only `.` and `./canonicalize`.
160
+
116
161
  ## Canonical form
117
162
 
118
163
  The canonical form is recursive JCS-lite JSON:
@@ -146,4 +191,4 @@ in this package returns `valid: true` only when BOTH succeed.
146
191
 
147
192
  ## License
148
193
 
149
- MIT see [`LICENSE`](./LICENSE).
194
+ MIT, see [`LICENSE`](./LICENSE).
@@ -30,57 +30,16 @@
30
30
  * Identity stability: signatures live in a detached `signatures[]` array and
31
31
  * are NEVER part of the OID hash input (see `cdroContentCore` in oid.ts), so
32
32
  * adding, rotating, or replacing a signature never changes the object's OID.
33
- */
34
- import type { AttestationEnvelope } from './types.js';
35
- /** The single supported algorithm identifiers for the JSON profile. */
36
- export declare const ALG_ED25519 = "ed25519";
37
- export declare const ALG_ML_DSA_65 = "ml-dsa-65";
38
- /**
39
- * The DSSE Pre-Authentication Encoding (PAE).
40
- *
41
- * Per the DSSE spec:
42
- *
43
- * PAE(type, body) = "DSSEv1" SP LEN(type) SP type SP LEN(body) SP body
44
- *
45
- * where:
46
- * - SP is a single ASCII space (0x20),
47
- * - LEN(x) is the ASCII-decimal byte length of x's UTF-8 encoding,
48
- * - type and body are the raw UTF-8 bytes (NOT base64).
49
33
  *
50
- * Binding the payloadType (and both lengths) into the signed bytes is what
51
- * makes a signature non-transferable across object types. Returns the raw
52
- * bytes to be signed/verified.
34
+ * NODE ENTRY: this module verifies via node:crypto (Ed25519 native, ML-DSA-65
35
+ * native-with-@noble-fallback) and is SYNCHRONOUS. The PAE, algorithm ids,
36
+ * types, AND-policy lookup, and envelope-shape predicate are shared with the
37
+ * browser verify surface via ./internal/attestation-core.ts (pure, no
38
+ * node:crypto); only the verify orchestration below is node-specific. The
39
+ * browser-safe async equivalent lives in ./verify-browser.ts.
53
40
  */
54
- export declare function pae(payloadType: string, payload: string | Uint8Array): Uint8Array;
55
- export interface VerifyAttestationInput {
56
- /**
57
- * The DSSE attestation envelope to verify. Its `payloadType` is bound into
58
- * the PAE; its `payload` is the canonical UTF-8 string that was signed.
59
- */
60
- envelope: AttestationEnvelope;
61
- /** Raw 32-byte Ed25519 public key. */
62
- ed25519_pub: Uint8Array;
63
- /** Raw ML-DSA-65 public key bytes. */
64
- ml_dsa_pub: Uint8Array;
65
- /**
66
- * Optional. If supplied, the verifier asserts the envelope's `payloadType`
67
- * equals this value before verifying signatures — an explicit type-pinning
68
- * check on top of the structural PAE binding. A mismatch fails with
69
- * `payload-type-mismatch`.
70
- */
71
- expectedPayloadType?: string;
72
- }
73
- export interface VerifyAttestationResult {
74
- /** True only when BOTH required signatures verified over the same PAE. */
75
- valid: boolean;
76
- /**
77
- * Human-readable failure reasons. Empty when valid. Possible values:
78
- * 'envelope-malformed', 'payload-type-mismatch', 'missing-ed25519',
79
- * 'missing-ml-dsa-65', 'ed25519-malformed', 'ml-dsa-malformed',
80
- * 'ed25519-invalid', 'ml-dsa-invalid'.
81
- */
82
- reasons: string[];
83
- }
41
+ import { ALG_ED25519, ALG_ML_DSA_65, pae, type VerifyAttestationInput, type VerifyAttestationResult } from './internal/attestation-core.js';
42
+ export { ALG_ED25519, ALG_ML_DSA_65, pae, type VerifyAttestationInput, type VerifyAttestationResult, };
84
43
  /**
85
44
  * Verify a hybrid DSSE attestation envelope. Returns `valid: true` only when
86
45
  * the envelope carries BOTH an `ed25519` and an `ml-dsa-65` signature and
@@ -1 +1 @@
1
- {"version":3,"file":"attestation.d.ts","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAKH,OAAO,KAAK,EAAE,mBAAmB,EAAwB,MAAM,YAAY,CAAA;AAE3E,uEAAuE;AACvE,eAAO,MAAM,WAAW,YAAY,CAAA;AACpC,eAAO,MAAM,aAAa,cAAc,CAAA;AAExC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,CAYjF;AAED,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,sCAAsC;IACtC,WAAW,EAAE,UAAU,CAAA;IACvB,sCAAsC;IACtC,UAAU,EAAE,UAAU,CAAA;IACtB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,0EAA0E;IAC1E,KAAK,EAAE,OAAO,CAAA;IACd;;;;;OAKG;IACH,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,uBAAuB,CAsExF"}
1
+ {"version":3,"file":"attestation.d.ts","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAKH,OAAO,EACL,WAAW,EACX,aAAa,EAGb,GAAG,EACH,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC7B,MAAM,gCAAgC,CAAA;AAIvC,OAAO,EACL,WAAW,EACX,aAAa,EACb,GAAG,EACH,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,GAC7B,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,uBAAuB,CA+DxF"}
@@ -30,39 +30,21 @@
30
30
  * Identity stability: signatures live in a detached `signatures[]` array and
31
31
  * are NEVER part of the OID hash input (see `cdroContentCore` in oid.ts), so
32
32
  * adding, rotating, or replacing a signature never changes the object's OID.
33
+ *
34
+ * NODE ENTRY: this module verifies via node:crypto (Ed25519 native, ML-DSA-65
35
+ * native-with-@noble-fallback) and is SYNCHRONOUS. The PAE, algorithm ids,
36
+ * types, AND-policy lookup, and envelope-shape predicate are shared with the
37
+ * browser verify surface via ./internal/attestation-core.ts (pure, no
38
+ * node:crypto); only the verify orchestration below is node-specific. The
39
+ * browser-safe async equivalent lives in ./verify-browser.ts.
33
40
  */
34
41
  import { verifyEd25519 } from './ed25519.js';
35
42
  import { decodeBase64Strict } from './internal/base64.js';
36
43
  import { verifyMlDsa65 } from './mldsa.js';
37
- /** The single supported algorithm identifiers for the JSON profile. */
38
- export const ALG_ED25519 = 'ed25519';
39
- export const ALG_ML_DSA_65 = 'ml-dsa-65';
40
- /**
41
- * The DSSE Pre-Authentication Encoding (PAE).
42
- *
43
- * Per the DSSE spec:
44
- *
45
- * PAE(type, body) = "DSSEv1" SP LEN(type) SP type SP LEN(body) SP body
46
- *
47
- * where:
48
- * - SP is a single ASCII space (0x20),
49
- * - LEN(x) is the ASCII-decimal byte length of x's UTF-8 encoding,
50
- * - type and body are the raw UTF-8 bytes (NOT base64).
51
- *
52
- * Binding the payloadType (and both lengths) into the signed bytes is what
53
- * makes a signature non-transferable across object types. Returns the raw
54
- * bytes to be signed/verified.
55
- */
56
- export function pae(payloadType, payload) {
57
- const enc = new TextEncoder();
58
- const typeBytes = enc.encode(payloadType);
59
- const bodyBytes = typeof payload === 'string' ? enc.encode(payload) : payload;
60
- const prefix = enc.encode(`DSSEv1 ${typeBytes.length} ${payloadType} ${bodyBytes.length} `);
61
- const out = new Uint8Array(prefix.length + bodyBytes.length);
62
- out.set(prefix, 0);
63
- out.set(bodyBytes, prefix.length);
64
- return out;
65
- }
44
+ import { ALG_ED25519, ALG_ML_DSA_65, findSig, isWellFormedEnvelope, pae, } from './internal/attestation-core.js';
45
+ // Re-export the crypto-agnostic core so the public surface is unchanged:
46
+ // `import { pae, ALG_ED25519, ALG_ML_DSA_65 } from '@synoi/sraid'` still works.
47
+ export { ALG_ED25519, ALG_ML_DSA_65, pae, };
66
48
  /**
67
49
  * Verify a hybrid DSSE attestation envelope. Returns `valid: true` only when
68
50
  * the envelope carries BOTH an `ed25519` and an `ml-dsa-65` signature and
@@ -73,12 +55,7 @@ export function pae(payloadType, payload) {
73
55
  export function verifyAttestation(input) {
74
56
  const reasons = [];
75
57
  const env = input.envelope;
76
- if (env === null ||
77
- typeof env !== 'object' ||
78
- typeof env.payloadType !== 'string' ||
79
- env.payloadType.length === 0 ||
80
- typeof env.payload !== 'string' ||
81
- !Array.isArray(env.signatures)) {
58
+ if (!isWellFormedEnvelope(env)) {
82
59
  return { valid: false, reasons: ['envelope-malformed'] };
83
60
  }
84
61
  if (input.expectedPayloadType !== undefined &&
@@ -137,18 +114,10 @@ export function verifyAttestation(input) {
137
114
  return { valid: edOk && mlOk && !!edEntry && !!mlEntry, reasons };
138
115
  }
139
116
  // ── Helpers ───────────────────────────────────────────────────────────────────
140
- function findSig(sigs, alg) {
141
- for (const s of sigs) {
142
- if (s && typeof s === 'object' && s.alg === alg && typeof s.sig === 'string') {
143
- return s;
144
- }
145
- }
146
- return undefined;
147
- }
148
117
  // Strict standard base64. Throws Error('base64-malformed') on any deviation
149
- // (illegal char, bad length, bad padding) rather than silently truncating.
150
- // Both call sites wrap this in try/catch and map the throw to a *-malformed
151
- // reason, so verifyAttestation never throws.
118
+ // (illegal char, bad length, bad padding) rather than silently truncating. The
119
+ // call sites wrap this in try/catch and map the throw to a *-malformed reason,
120
+ // so verifyAttestation never throws.
152
121
  function fromBase64(s) {
153
122
  return decodeBase64Strict(s);
154
123
  }
@@ -1 +1 @@
1
- {"version":3,"file":"attestation.js","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attestation.js","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @synoi/sraid — internal/attestation-core.ts
3
+ *
4
+ * The crypto-agnostic core of the L2 DSSE attestation profile: the PAE
5
+ * encoding, the algorithm identifiers, the public verify input/result types,
6
+ * the both-required signature lookup, and the envelope-shape predicate.
7
+ *
8
+ * This is a PURE module — no crypto, no node:crypto, no Buffer — so the same
9
+ * PAE bytes, AND-policy shape, and type contract are shared byte-for-byte by
10
+ * BOTH the node default entry (attestation.ts, node:crypto verifiers) and the
11
+ * browser verify surface (verify-browser.ts, WebCrypto + @noble verifiers).
12
+ * Divergent PAE or shape logic between the two would be a signature-validity
13
+ * hazard; keeping it in one place removes that risk.
14
+ *
15
+ * The verify ORCHESTRATION (decode each sig, run both verifiers, gate on the
16
+ * AND policy) is intentionally NOT here: the node path is synchronous
17
+ * (node:crypto verify) and the browser path is asynchronous (WebCrypto verify),
18
+ * so a single shared body cannot serve both. Each entry keeps its own thin
19
+ * orchestration and reuses these leaf helpers.
20
+ */
21
+ import type { AttestationEnvelope, AttestationSignature } from '../types.js';
22
+ /** The single supported algorithm identifiers for the JSON profile. */
23
+ export declare const ALG_ED25519 = "ed25519";
24
+ export declare const ALG_ML_DSA_65 = "ml-dsa-65";
25
+ /**
26
+ * The DSSE Pre-Authentication Encoding (PAE).
27
+ *
28
+ * Per the DSSE spec:
29
+ *
30
+ * PAE(type, body) = "DSSEv1" SP LEN(type) SP type SP LEN(body) SP body
31
+ *
32
+ * where:
33
+ * - SP is a single ASCII space (0x20),
34
+ * - LEN(x) is the ASCII-decimal byte length of x's UTF-8 encoding,
35
+ * - type and body are the raw UTF-8 bytes (NOT base64).
36
+ *
37
+ * Binding the payloadType (and both lengths) into the signed bytes is what
38
+ * makes a signature non-transferable across object types. Returns the raw
39
+ * bytes to be signed/verified.
40
+ */
41
+ export declare function pae(payloadType: string, payload: string | Uint8Array): Uint8Array;
42
+ export interface VerifyAttestationInput {
43
+ /**
44
+ * The DSSE attestation envelope to verify. Its `payloadType` is bound into
45
+ * the PAE; its `payload` is the canonical UTF-8 string that was signed.
46
+ */
47
+ envelope: AttestationEnvelope;
48
+ /** Raw 32-byte Ed25519 public key. */
49
+ ed25519_pub: Uint8Array;
50
+ /** Raw ML-DSA-65 public key bytes. */
51
+ ml_dsa_pub: Uint8Array;
52
+ /**
53
+ * Optional. If supplied, the verifier asserts the envelope's `payloadType`
54
+ * equals this value before verifying signatures — an explicit type-pinning
55
+ * check on top of the structural PAE binding. A mismatch fails with
56
+ * `payload-type-mismatch`.
57
+ */
58
+ expectedPayloadType?: string;
59
+ }
60
+ export interface VerifyAttestationResult {
61
+ /** True only when BOTH required signatures verified over the same PAE. */
62
+ valid: boolean;
63
+ /**
64
+ * Human-readable failure reasons. Empty when valid. Possible values:
65
+ * 'envelope-malformed', 'payload-type-mismatch', 'missing-ed25519',
66
+ * 'missing-ml-dsa-65', 'ed25519-malformed', 'ml-dsa-malformed',
67
+ * 'ed25519-invalid', 'ml-dsa-invalid'.
68
+ */
69
+ reasons: string[];
70
+ }
71
+ /**
72
+ * Structural well-formedness of an attestation envelope: a non-null object with
73
+ * a non-empty string `payloadType`, a string `payload`, and an array
74
+ * `signatures`. A false result maps to the `envelope-malformed` reason. This is
75
+ * the SINGLE definition of envelope shape, shared by the node and browser
76
+ * verifiers so they reject the same inputs.
77
+ */
78
+ export declare function isWellFormedEnvelope(env: unknown): env is AttestationEnvelope;
79
+ /**
80
+ * Find the first well-formed signature entry for `alg` (a `{ alg, sig }` object
81
+ * whose `sig` is a string). Returns undefined when absent; the AND policy in
82
+ * each verifier turns a missing pair member into a `missing-*` reason.
83
+ */
84
+ export declare function findSig(sigs: readonly AttestationSignature[], alg: string): AttestationSignature | undefined;
85
+ //# sourceMappingURL=attestation-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attestation-core.d.ts","sourceRoot":"","sources":["../../src/internal/attestation-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAE5E,uEAAuE;AACvE,eAAO,MAAM,WAAW,YAAY,CAAA;AACpC,eAAO,MAAM,aAAa,cAAc,CAAA;AAExC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,CAYjF;AAED,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,sCAAsC;IACtC,WAAW,EAAE,UAAU,CAAA;IACvB,sCAAsC;IACtC,UAAU,EAAE,UAAU,CAAA;IACtB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,0EAA0E;IAC1E,KAAK,EAAE,OAAO,CAAA;IACd;;;;;OAKG;IACH,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,mBAAmB,CAS7E;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,SAAS,oBAAoB,EAAE,EACrC,GAAG,EAAE,MAAM,GACV,oBAAoB,GAAG,SAAS,CAOlC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @synoi/sraid — internal/attestation-core.ts
3
+ *
4
+ * The crypto-agnostic core of the L2 DSSE attestation profile: the PAE
5
+ * encoding, the algorithm identifiers, the public verify input/result types,
6
+ * the both-required signature lookup, and the envelope-shape predicate.
7
+ *
8
+ * This is a PURE module — no crypto, no node:crypto, no Buffer — so the same
9
+ * PAE bytes, AND-policy shape, and type contract are shared byte-for-byte by
10
+ * BOTH the node default entry (attestation.ts, node:crypto verifiers) and the
11
+ * browser verify surface (verify-browser.ts, WebCrypto + @noble verifiers).
12
+ * Divergent PAE or shape logic between the two would be a signature-validity
13
+ * hazard; keeping it in one place removes that risk.
14
+ *
15
+ * The verify ORCHESTRATION (decode each sig, run both verifiers, gate on the
16
+ * AND policy) is intentionally NOT here: the node path is synchronous
17
+ * (node:crypto verify) and the browser path is asynchronous (WebCrypto verify),
18
+ * so a single shared body cannot serve both. Each entry keeps its own thin
19
+ * orchestration and reuses these leaf helpers.
20
+ */
21
+ /** The single supported algorithm identifiers for the JSON profile. */
22
+ export const ALG_ED25519 = 'ed25519';
23
+ export const ALG_ML_DSA_65 = 'ml-dsa-65';
24
+ /**
25
+ * The DSSE Pre-Authentication Encoding (PAE).
26
+ *
27
+ * Per the DSSE spec:
28
+ *
29
+ * PAE(type, body) = "DSSEv1" SP LEN(type) SP type SP LEN(body) SP body
30
+ *
31
+ * where:
32
+ * - SP is a single ASCII space (0x20),
33
+ * - LEN(x) is the ASCII-decimal byte length of x's UTF-8 encoding,
34
+ * - type and body are the raw UTF-8 bytes (NOT base64).
35
+ *
36
+ * Binding the payloadType (and both lengths) into the signed bytes is what
37
+ * makes a signature non-transferable across object types. Returns the raw
38
+ * bytes to be signed/verified.
39
+ */
40
+ export function pae(payloadType, payload) {
41
+ const enc = new TextEncoder();
42
+ const typeBytes = enc.encode(payloadType);
43
+ const bodyBytes = typeof payload === 'string' ? enc.encode(payload) : payload;
44
+ const prefix = enc.encode(`DSSEv1 ${typeBytes.length} ${payloadType} ${bodyBytes.length} `);
45
+ const out = new Uint8Array(prefix.length + bodyBytes.length);
46
+ out.set(prefix, 0);
47
+ out.set(bodyBytes, prefix.length);
48
+ return out;
49
+ }
50
+ /**
51
+ * Structural well-formedness of an attestation envelope: a non-null object with
52
+ * a non-empty string `payloadType`, a string `payload`, and an array
53
+ * `signatures`. A false result maps to the `envelope-malformed` reason. This is
54
+ * the SINGLE definition of envelope shape, shared by the node and browser
55
+ * verifiers so they reject the same inputs.
56
+ */
57
+ export function isWellFormedEnvelope(env) {
58
+ return (env !== null &&
59
+ typeof env === 'object' &&
60
+ typeof env.payloadType === 'string' &&
61
+ env.payloadType.length > 0 &&
62
+ typeof env.payload === 'string' &&
63
+ Array.isArray(env.signatures));
64
+ }
65
+ /**
66
+ * Find the first well-formed signature entry for `alg` (a `{ alg, sig }` object
67
+ * whose `sig` is a string). Returns undefined when absent; the AND policy in
68
+ * each verifier turns a missing pair member into a `missing-*` reason.
69
+ */
70
+ export function findSig(sigs, alg) {
71
+ for (const s of sigs) {
72
+ if (s && typeof s === 'object' && s.alg === alg && typeof s.sig === 'string') {
73
+ return s;
74
+ }
75
+ }
76
+ return undefined;
77
+ }
78
+ //# sourceMappingURL=attestation-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attestation-core.js","sourceRoot":"","sources":["../../src/internal/attestation-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,uEAAuE;AACvE,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAA;AACpC,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAA;AAExC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,GAAG,CAAC,WAAmB,EAAE,OAA4B;IACnE,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAA;IAC7B,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;IACzC,MAAM,SAAS,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;IAE7E,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CACvB,UAAU,SAAS,CAAC,MAAM,IAAI,WAAW,IAAI,SAAS,CAAC,MAAM,GAAG,CACjE,CAAA;IACD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;IAC5D,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAClB,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;IACjC,OAAO,GAAG,CAAA;AACZ,CAAC;AAiCD;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAY;IAC/C,OAAO,CACL,GAAG,KAAK,IAAI;QACZ,OAAO,GAAG,KAAK,QAAQ;QACvB,OAAQ,GAA2B,CAAC,WAAW,KAAK,QAAQ;QAC3D,GAA2B,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;QACnD,OAAQ,GAA2B,CAAC,OAAO,KAAK,QAAQ;QACxD,KAAK,CAAC,OAAO,CAAE,GAA2B,CAAC,UAAU,CAAC,CACvD,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CACrB,IAAqC,EACrC,GAAW;IAEX,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC7E,OAAO,CAAC,CAAA;QACV,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @synoi/sraid — internal/base64-browser.ts
3
+ *
4
+ * Strict standard-base64 (RFC 4648 §4, with `=` padding) decode for the
5
+ * BROWSER verify surface.
6
+ *
7
+ * INTERNAL: not exported from any public entry.
8
+ *
9
+ * Why a separate decoder: the node decoder (./base64.ts `decodeBase64Strict`)
10
+ * finishes with `Buffer.from(s, 'base64')`. `Buffer` is a NODE global that does
11
+ * not exist in a browser / service-worker / Chrome-extension context — and,
12
+ * crucially, it is NOT a `node:` specifier, so an esbuild `node:`-reference scan
13
+ * would NOT catch it. A `Buffer`-based decode would bundle clean yet throw
14
+ * `ReferenceError: Buffer is not defined` at runtime. This decoder is
15
+ * Buffer-free: it reuses the pure `assertBase64` validation (regex/length/
16
+ * padding only, from ./base64-validate.js — NOT ./base64.js, whose graph pulls
17
+ * in the `Buffer` decode) and decodes via the standard `atob` global, which IS
18
+ * available in browsers, dedicated/shared workers, and MV3 extension service
19
+ * workers.
20
+ *
21
+ * Same strict contract as the node path: throws Error('base64-malformed') on
22
+ * any deviation (illegal char, bad length, bad padding) rather than silently
23
+ * truncating, so a malformed signature is rejected loudly, never decoded to a
24
+ * short prefix that then "verifies" against something unintended.
25
+ */
26
+ /**
27
+ * Strictly decode standard base64 to raw bytes using only browser-safe globals.
28
+ * Throws Error('base64-malformed') on any non-conforming input.
29
+ */
30
+ export declare function decodeBase64StrictBrowser(s: string): Uint8Array;
31
+ //# sourceMappingURL=base64-browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64-browser.d.ts","sourceRoot":"","sources":["../../src/internal/base64-browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAM/D"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @synoi/sraid — internal/base64-browser.ts
3
+ *
4
+ * Strict standard-base64 (RFC 4648 §4, with `=` padding) decode for the
5
+ * BROWSER verify surface.
6
+ *
7
+ * INTERNAL: not exported from any public entry.
8
+ *
9
+ * Why a separate decoder: the node decoder (./base64.ts `decodeBase64Strict`)
10
+ * finishes with `Buffer.from(s, 'base64')`. `Buffer` is a NODE global that does
11
+ * not exist in a browser / service-worker / Chrome-extension context — and,
12
+ * crucially, it is NOT a `node:` specifier, so an esbuild `node:`-reference scan
13
+ * would NOT catch it. A `Buffer`-based decode would bundle clean yet throw
14
+ * `ReferenceError: Buffer is not defined` at runtime. This decoder is
15
+ * Buffer-free: it reuses the pure `assertBase64` validation (regex/length/
16
+ * padding only, from ./base64-validate.js — NOT ./base64.js, whose graph pulls
17
+ * in the `Buffer` decode) and decodes via the standard `atob` global, which IS
18
+ * available in browsers, dedicated/shared workers, and MV3 extension service
19
+ * workers.
20
+ *
21
+ * Same strict contract as the node path: throws Error('base64-malformed') on
22
+ * any deviation (illegal char, bad length, bad padding) rather than silently
23
+ * truncating, so a malformed signature is rejected loudly, never decoded to a
24
+ * short prefix that then "verifies" against something unintended.
25
+ */
26
+ import { assertBase64 } from './base64-validate.js';
27
+ /**
28
+ * Strictly decode standard base64 to raw bytes using only browser-safe globals.
29
+ * Throws Error('base64-malformed') on any non-conforming input.
30
+ */
31
+ export function decodeBase64StrictBrowser(s) {
32
+ assertBase64(s);
33
+ const bin = atob(s);
34
+ const out = new Uint8Array(bin.length);
35
+ for (let i = 0; i < bin.length; i++)
36
+ out[i] = bin.charCodeAt(i);
37
+ return out;
38
+ }
39
+ //# sourceMappingURL=base64-browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64-browser.js","sourceRoot":"","sources":["../../src/internal/base64-browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnD;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,CAAS;IACjD,YAAY,CAAC,CAAC,CAAC,CAAA;IACf,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IACnB,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;IAC/D,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @synoi/sraid — internal/base64-validate.ts
3
+ *
4
+ * Pure strict standard-base64 (RFC 4648 §4, with `=` padding) VALIDATION.
5
+ *
6
+ * INTERNAL: not exported from any public entry.
7
+ *
8
+ * This is the Buffer-free, node:crypto-free half of base64 handling, split out
9
+ * so the browser decoder (./base64-browser.ts) can validate without importing
10
+ * ./base64.ts (whose `decodeBase64Strict` uses the node `Buffer` global — safe
11
+ * in node, a runtime `ReferenceError` in a browser). The node decoder re-uses
12
+ * this same validator, so both paths reject exactly the same inputs.
13
+ */
14
+ /**
15
+ * Validate strict standard base64. Throws Error('base64-malformed') on any
16
+ * non-conforming input.
17
+ */
18
+ export declare function assertBase64(s: string): void;
19
+ //# sourceMappingURL=base64-validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64-validate.d.ts","sourceRoot":"","sources":["../../src/internal/base64-validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAQ5C"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @synoi/sraid — internal/base64-validate.ts
3
+ *
4
+ * Pure strict standard-base64 (RFC 4648 §4, with `=` padding) VALIDATION.
5
+ *
6
+ * INTERNAL: not exported from any public entry.
7
+ *
8
+ * This is the Buffer-free, node:crypto-free half of base64 handling, split out
9
+ * so the browser decoder (./base64-browser.ts) can validate without importing
10
+ * ./base64.ts (whose `decodeBase64Strict` uses the node `Buffer` global — safe
11
+ * in node, a runtime `ReferenceError` in a browser). The node decoder re-uses
12
+ * this same validator, so both paths reject exactly the same inputs.
13
+ */
14
+ // Standard base64 alphabet; `=` only as 1-2 trailing pad chars. Total length
15
+ // must be a multiple of 4.
16
+ const STD_B64 = /^[A-Za-z0-9+/]*={0,2}$/;
17
+ /**
18
+ * Validate strict standard base64. Throws Error('base64-malformed') on any
19
+ * non-conforming input.
20
+ */
21
+ export function assertBase64(s) {
22
+ if (typeof s !== 'string')
23
+ throw new Error('base64-malformed');
24
+ if (s.length % 4 !== 0)
25
+ throw new Error('base64-malformed');
26
+ if (!STD_B64.test(s))
27
+ throw new Error('base64-malformed');
28
+ // `=` may appear only in the final 1-2 positions. The regex `={0,2}` anchored
29
+ // at end already guarantees pad chars are contiguous and trailing, but a pad
30
+ // char earlier in the body would have been rejected by the alphabet class —
31
+ // so an in-body `=` cannot pass STD_B64. No extra check needed.
32
+ }
33
+ //# sourceMappingURL=base64-validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64-validate.js","sourceRoot":"","sources":["../../src/internal/base64-validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,6EAA6E;AAC7E,2BAA2B;AAC3B,MAAM,OAAO,GAAG,wBAAwB,CAAA;AAExC;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;IAC9D,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;IAC3D,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;IACzD,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,gEAAgE;AAClE,CAAC"}
@@ -14,12 +14,14 @@
14
14
  *
15
15
  * Canonical inputs carry no surrounding whitespace, so no trim is applied;
16
16
  * leading/trailing whitespace is itself a rejection.
17
+ *
18
+ * The strict VALIDATION (alphabet/length/padding) lives in the pure, Buffer-
19
+ * free ./base64-validate.ts so the browser decoder can share it; this module
20
+ * adds the node `Buffer`-based decode on top and re-exports `assertBase64` so
21
+ * existing importers of `./base64.js` are unchanged.
17
22
  */
18
- /**
19
- * Validate strict standard base64. Throws Error('base64-malformed') on any
20
- * non-conforming input.
21
- */
22
- export declare function assertBase64(s: string): void;
23
+ import { assertBase64 } from './base64-validate.js';
24
+ export { assertBase64 };
23
25
  /**
24
26
  * Strictly decode standard base64 to raw bytes. Throws
25
27
  * Error('base64-malformed') on any non-conforming input.
@@ -1 +1 @@
1
- {"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../../src/internal/base64.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAQ5C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAGxD"}
1
+ {"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../../src/internal/base64.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnD,OAAO,EAAE,YAAY,EAAE,CAAA;AAEvB;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAGxD"}
@@ -14,26 +14,14 @@
14
14
  *
15
15
  * Canonical inputs carry no surrounding whitespace, so no trim is applied;
16
16
  * leading/trailing whitespace is itself a rejection.
17
+ *
18
+ * The strict VALIDATION (alphabet/length/padding) lives in the pure, Buffer-
19
+ * free ./base64-validate.ts so the browser decoder can share it; this module
20
+ * adds the node `Buffer`-based decode on top and re-exports `assertBase64` so
21
+ * existing importers of `./base64.js` are unchanged.
17
22
  */
18
- // Standard base64 alphabet; `=` only as 1-2 trailing pad chars. Total length
19
- // must be a multiple of 4.
20
- const STD_B64 = /^[A-Za-z0-9+/]*={0,2}$/;
21
- /**
22
- * Validate strict standard base64. Throws Error('base64-malformed') on any
23
- * non-conforming input.
24
- */
25
- export function assertBase64(s) {
26
- if (typeof s !== 'string')
27
- throw new Error('base64-malformed');
28
- if (s.length % 4 !== 0)
29
- throw new Error('base64-malformed');
30
- if (!STD_B64.test(s))
31
- throw new Error('base64-malformed');
32
- // `=` may appear only in the final 1-2 positions. The regex `={0,2}` anchored
33
- // at end already guarantees pad chars are contiguous and trailing, but a pad
34
- // char earlier in the body would have been rejected by the alphabet class —
35
- // so an in-body `=` cannot pass STD_B64. No extra check needed.
36
- }
23
+ import { assertBase64 } from './base64-validate.js';
24
+ export { assertBase64 };
37
25
  /**
38
26
  * Strictly decode standard base64 to raw bytes. Throws
39
27
  * Error('base64-malformed') on any non-conforming input.