@synoi/sraid 0.2.0 → 0.4.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/README.md +156 -50
- package/SPEC.md +2 -2
- package/dist/attestation.d.ts +8 -49
- package/dist/attestation.d.ts.map +1 -1
- package/dist/attestation.js +15 -46
- package/dist/attestation.js.map +1 -1
- package/dist/capability.d.ts +59 -0
- package/dist/capability.d.ts.map +1 -0
- package/dist/capability.js +44 -0
- package/dist/capability.js.map +1 -0
- package/dist/index.d.ts +13 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -18
- package/dist/index.js.map +1 -1
- package/dist/internal/attestation-core.d.ts +85 -0
- package/dist/internal/attestation-core.d.ts.map +1 -0
- package/dist/internal/attestation-core.js +78 -0
- package/dist/internal/attestation-core.js.map +1 -0
- package/dist/internal/base64-browser.d.ts +31 -0
- package/dist/internal/base64-browser.d.ts.map +1 -0
- package/dist/internal/base64-browser.js +39 -0
- package/dist/internal/base64-browser.js.map +1 -0
- package/dist/internal/base64-validate.d.ts +19 -0
- package/dist/internal/base64-validate.d.ts.map +1 -0
- package/dist/internal/base64-validate.js +33 -0
- package/dist/internal/base64-validate.js.map +1 -0
- package/dist/internal/base64.d.ts +7 -5
- package/dist/internal/base64.d.ts.map +1 -1
- package/dist/internal/base64.js +7 -19
- package/dist/internal/base64.js.map +1 -1
- package/dist/internal/content-core.d.ts +68 -0
- package/dist/internal/content-core.d.ts.map +1 -0
- package/dist/internal/content-core.js +87 -0
- package/dist/internal/content-core.js.map +1 -0
- package/dist/internal/ed25519-browser.d.ts +33 -0
- package/dist/internal/ed25519-browser.d.ts.map +1 -0
- package/dist/internal/ed25519-browser.js +79 -0
- package/dist/internal/ed25519-browser.js.map +1 -0
- package/dist/internal/mldsa-browser.d.ts +27 -0
- package/dist/internal/mldsa-browser.d.ts.map +1 -0
- package/dist/internal/mldsa-browser.js +35 -0
- package/dist/internal/mldsa-browser.js.map +1 -0
- package/dist/internal/sha256-browser.d.ts +17 -0
- package/dist/internal/sha256-browser.d.ts.map +1 -0
- package/dist/internal/sha256-browser.js +27 -0
- package/dist/internal/sha256-browser.js.map +1 -0
- package/dist/lineage.js +0 -0
- package/dist/lineage.js.map +1 -1
- package/dist/oid.d.ts +2 -52
- package/dist/oid.d.ts.map +1 -1
- package/dist/oid.js +9 -71
- package/dist/oid.js.map +1 -1
- package/dist/types.d.ts +4 -30
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -1
- package/dist/validate.d.ts +2 -17
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +0 -57
- package/dist/validate.js.map +1 -1
- package/dist/verify-browser.d.ts +80 -0
- package/dist/verify-browser.d.ts.map +1 -0
- package/dist/verify-browser.js +157 -0
- package/dist/verify-browser.js.map +1 -0
- package/package.json +22 -3
- package/src/attestation.ts +29 -88
- package/src/capability.ts +69 -0
- package/src/index.ts +27 -36
- package/src/internal/attestation-core.ts +122 -0
- package/src/internal/base64-browser.ts +39 -0
- package/src/internal/base64-validate.ts +31 -0
- package/src/internal/base64.ts +7 -16
- package/src/internal/content-core.ts +88 -0
- package/src/internal/ed25519-browser.ts +85 -0
- package/src/internal/mldsa-browser.ts +39 -0
- package/src/internal/sha256-browser.ts +28 -0
- package/src/lineage.ts +0 -0
- package/src/oid.ts +10 -73
- package/src/types.ts +23 -31
- package/src/validate.ts +1 -64
- package/src/verify-browser.ts +185 -0
- package/dist/authority.d.ts +0 -351
- package/dist/authority.d.ts.map +0 -1
- package/dist/authority.js +0 -563
- package/dist/authority.js.map +0 -1
- package/src/authority.ts +0 -849
package/README.md
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# @synoi/sraid
|
|
2
2
|
|
|
3
|
-
Reference TypeScript implementation of **SRAID
|
|
4
|
-
|
|
3
|
+
Reference TypeScript implementation of **SRAID**, the SynOI content-addressed
|
|
4
|
+
object format. MIT-licensed.
|
|
5
|
+
|
|
6
|
+
SRAID is a name, not an acronym. It has been expanded two different ways in two
|
|
7
|
+
different places; neither expansion added anything, so this package no longer
|
|
8
|
+
expands it. What it does is below.
|
|
5
9
|
|
|
6
10
|
SRAID defines:
|
|
7
11
|
|
|
@@ -9,24 +13,28 @@ SRAID defines:
|
|
|
9
13
|
- a deterministic canonical object serializer used as input to hashing and signing,
|
|
10
14
|
- a content-derived **OID** (`sha256:` followed by 64 hex chars) over the canonical bytes,
|
|
11
15
|
- a hybrid **Ed25519 + ML-DSA-65** signature envelope,
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
- a **Merkle-DAG lineage** layer (`prev` edge + typed `links[]`), hashed into the OID so a head id commits its whole reachable history.
|
|
17
|
+
|
|
18
|
+
Supersession is expressed by two things, both identity-bound because
|
|
19
|
+
`cdroContentCore` hashes them: the `prev`/`links[]` Merkle edges, and the legacy
|
|
20
|
+
self-asserted `supersedes` string. `latestWins` resolves a version set to its
|
|
21
|
+
single head by following the identity-bound edges, giving a verifier a
|
|
22
|
+
**latest-wins / monotone** rule instead of an unwitnessed pointer. Given the
|
|
23
|
+
complete version set it will not name a superseded object as head; full
|
|
24
|
+
rollback-replay resistance additionally requires that the superseding object
|
|
25
|
+
cannot be withheld, which is a Resolver / transparency-log property (DESIGN, not
|
|
26
|
+
yet deployed). `set_complete` on `LatestWinsResult` is the local signal letting a
|
|
27
|
+
caller detect an incomplete (potentially withheld) set.
|
|
28
|
+
|
|
29
|
+
The standalone **SRO** was removed in 0.4.0 — see the CHANGELOG.
|
|
30
|
+
|
|
31
|
+
This package is object identity: canonical bytes, OIDs, hybrid signature
|
|
32
|
+
verification, lineage. It has no storage, no HTTP surface, and no policy.
|
|
33
|
+
|
|
34
|
+
It verifies **signatures, not identities**: `signer_kid` is an opaque string this
|
|
35
|
+
package never resolves, and nothing here checks revocation. Deciding whether a
|
|
36
|
+
key was trusted at signing time needs a key directory, which is a caller
|
|
37
|
+
concern.
|
|
30
38
|
|
|
31
39
|
## Install
|
|
32
40
|
|
|
@@ -39,63 +47,101 @@ npm install @synoi/sraid
|
|
|
39
47
|
```ts
|
|
40
48
|
import {
|
|
41
49
|
canonicalize,
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
cdroOid,
|
|
51
|
+
cdroContentCore,
|
|
52
|
+
pae,
|
|
53
|
+
verifyAttestation,
|
|
44
54
|
validateCdro,
|
|
55
|
+
type AttestationEnvelope,
|
|
45
56
|
type CDRO,
|
|
46
|
-
type SignatureEnvelope,
|
|
47
57
|
} from '@synoi/sraid'
|
|
48
58
|
import { ed25519 } from '@noble/curves/ed25519'
|
|
49
59
|
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js'
|
|
50
60
|
import { randomBytes } from 'node:crypto'
|
|
51
61
|
|
|
52
|
-
// 1. Build
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
const cdro: CDRO<typeof body> = {
|
|
57
|
-
oid,
|
|
62
|
+
// 1. Build the CDRO content core: every field EXCEPT the detached
|
|
63
|
+
// envelope fields (oid, signature, attestation, ...).
|
|
64
|
+
const core = {
|
|
58
65
|
type: 'gap:capability_declaration',
|
|
59
|
-
sraid_version: '2.0',
|
|
66
|
+
sraid_version: '2.0' as const,
|
|
60
67
|
tenant_id: 't-home',
|
|
61
68
|
created_at_ms: Date.now(),
|
|
62
69
|
created_by: 'actor:skill:demo',
|
|
63
|
-
body,
|
|
70
|
+
body: { capability: 'door.unlock', risk_class: 'B' },
|
|
64
71
|
}
|
|
65
72
|
|
|
66
|
-
|
|
73
|
+
// 2. Identity is the hash of the WHOLE content core, not just the body.
|
|
74
|
+
// Use cdroOid(core) — NOT oidOf(core.body).
|
|
75
|
+
const cdro: CDRO<typeof core.body> = { oid: cdroOid(core), ...core }
|
|
76
|
+
|
|
77
|
+
console.log(validateCdro(cdro)) // { ok: true, errors: [] }
|
|
78
|
+
console.log(cdro.oid === cdroOid(cdro)) // true
|
|
67
79
|
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
const
|
|
80
|
+
// 3. Sign the canonical content core through the DSSE PAE, which binds
|
|
81
|
+
// payloadType into the signed bytes (prevents cross-type replay).
|
|
82
|
+
const payloadType = 'application/vnd.synoi.sraid+json'
|
|
83
|
+
const payload = canonicalize(cdroContentCore(cdro))
|
|
84
|
+
const signedBytes = pae(payloadType, payload)
|
|
71
85
|
|
|
72
86
|
const edPriv = new Uint8Array(randomBytes(32))
|
|
73
87
|
const edPub = ed25519.getPublicKey(edPriv)
|
|
74
88
|
const mlKeys = ml_dsa65.keygen(new Uint8Array(randomBytes(32)))
|
|
75
89
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
90
|
+
const attestation: AttestationEnvelope = {
|
|
91
|
+
payloadType,
|
|
92
|
+
payload,
|
|
93
|
+
signatures: [
|
|
94
|
+
{
|
|
95
|
+
alg: 'ed25519',
|
|
96
|
+
sig: Buffer.from(ed25519.sign(signedBytes, edPriv)).toString('base64'),
|
|
97
|
+
keyid: 'synoi-demo-2026-05',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
alg: 'ml-dsa-65',
|
|
101
|
+
sig: Buffer.from(ml_dsa65.sign(signedBytes, mlKeys.secretKey)).toString('base64'),
|
|
102
|
+
keyid: 'synoi-demo-2026-05',
|
|
103
|
+
},
|
|
104
|
+
],
|
|
80
105
|
}
|
|
81
106
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
107
|
+
// 4. Attaching the attestation does NOT change identity — the six detached
|
|
108
|
+
// envelope fields are stripped before hashing.
|
|
109
|
+
const attested = { ...cdro, attestation }
|
|
110
|
+
console.log(cdroOid(attested) === cdro.oid) // true
|
|
111
|
+
|
|
112
|
+
// 5. Verify. `valid` is true only when BOTH signatures verify over the PAE.
|
|
113
|
+
const v = verifyAttestation({
|
|
114
|
+
envelope: attestation,
|
|
86
115
|
ed25519_pub: edPub,
|
|
87
116
|
ml_dsa_pub: mlKeys.publicKey,
|
|
117
|
+
expectedPayloadType: payloadType,
|
|
88
118
|
})
|
|
89
|
-
console.log(v)
|
|
119
|
+
console.log(v) // { valid: true, reasons: [] }
|
|
90
120
|
```
|
|
91
121
|
|
|
122
|
+
> **Identity is over the content core, not the body.** `oidOf(cdro.body)` is
|
|
123
|
+
> *not* a CDRO's OID; use `cdroOid`. `verifySignature` / `SignatureEnvelope`
|
|
124
|
+
> (bare-bytes, no payload-type binding) remain exported for compatibility but
|
|
125
|
+
> are superseded by the attestation path above.
|
|
126
|
+
|
|
92
127
|
## Surface
|
|
93
128
|
|
|
94
129
|
```ts
|
|
95
130
|
canonicalize(value: unknown): string
|
|
131
|
+
|
|
132
|
+
// CDRO identity — hash the content core. THIS is an object's OID.
|
|
133
|
+
cdroOid(cdro: unknown): string
|
|
134
|
+
cdroContentCore(cdro: unknown): Record<string, unknown>
|
|
135
|
+
CDRO_ENVELOPE_FIELDS: readonly string[] // the six stripped fields
|
|
136
|
+
|
|
137
|
+
// Value-level hashing. oidOf(cdro.body) is NOT the CDRO's OID.
|
|
96
138
|
oidOf(canonical: unknown): string
|
|
97
139
|
oidOfCanonical(canonical: string | Uint8Array): string
|
|
98
140
|
|
|
141
|
+
// DSSE attestation — payload-type bound, both signatures required.
|
|
142
|
+
verifyAttestation(input): { valid: boolean; reasons: string[] }
|
|
143
|
+
pae(payloadType: string, payload: string | Uint8Array): Uint8Array
|
|
144
|
+
|
|
99
145
|
verifySignature(args: {
|
|
100
146
|
canonical: string | Uint8Array
|
|
101
147
|
envelope: SignatureEnvelope
|
|
@@ -109,13 +155,69 @@ validateSignatureEnvelope(x: unknown): { ok: boolean; errors: string[] }
|
|
|
109
155
|
|
|
110
156
|
// Types
|
|
111
157
|
interface CDRO<TBody> { /* envelope */ }
|
|
112
|
-
interface SRO { /* supersession record */ }
|
|
113
158
|
interface SignatureEnvelope { ed25519: string; ml_dsa_65: string; signer_kid: string }
|
|
114
159
|
```
|
|
115
160
|
|
|
161
|
+
## Browser, Chrome extension, service worker
|
|
162
|
+
|
|
163
|
+
The default (`.`) entry statically imports `node:crypto` in three places
|
|
164
|
+
(`ed25519.ts`, `mldsa.ts`, `oid.ts`), so `import '@synoi/sraid'` breaks any
|
|
165
|
+
browser bundle. Import the `./verify-browser` subpath instead. It carries exactly
|
|
166
|
+
what a v2 hybrid DSSE receipt verifier needs, with no static `node:crypto`
|
|
167
|
+
anywhere in its graph.
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { verifyAttestation, canonicalize, cdroContentCore } from '@synoi/sraid/verify-browser'
|
|
171
|
+
|
|
172
|
+
const v = await verifyAttestation({
|
|
173
|
+
envelope: receipt.attestation,
|
|
174
|
+
ed25519_pub, // raw 32 bytes
|
|
175
|
+
ml_dsa_pub, // raw 1952 bytes
|
|
176
|
+
expectedPayloadType: 'application/vnd.synoi.gap+json',
|
|
177
|
+
})
|
|
178
|
+
// { valid: true, reasons: [] } only when BOTH signatures verify over the PAE
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
// The subpath surface. Four of these are ASYNC where the node entry is sync,
|
|
183
|
+
// because WebCrypto is Promise-based. Everything else is shared byte-for-byte
|
|
184
|
+
// with the node entry, so the two agree on the same input.
|
|
185
|
+
verifyAttestation(input): Promise<{ valid: boolean; reasons: string[] }> // ASYNC
|
|
186
|
+
cdroOid(cdro: unknown): Promise<string> // ASYNC
|
|
187
|
+
oidOf(canonical: unknown): Promise<string> // ASYNC
|
|
188
|
+
oidOfCanonical(canonical: string | Uint8Array): Promise<string> // ASYNC
|
|
189
|
+
|
|
190
|
+
canonicalize(value: unknown): string // pure, identical to the node entry
|
|
191
|
+
cdroContentCore(cdro: unknown): Record<string, unknown>
|
|
192
|
+
pae(payloadType: string, payload: string | Uint8Array): Uint8Array
|
|
193
|
+
CDRO_ENVELOPE_FIELDS: readonly string[]
|
|
194
|
+
ALG_ED25519, ALG_ML_DSA_65
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Crypto backends, since browsers have no native ML-DSA and no `node:crypto`:
|
|
198
|
+
Ed25519 verify on WebCrypto (RFC 8032 cofactored, matching the node path) with a
|
|
199
|
+
`@noble/curves` fallback below the WebCrypto-Ed25519 support floor; ML-DSA-65
|
|
200
|
+
verify on `@noble/post-quantum`; SHA-256 on WebCrypto `subtle.digest`.
|
|
201
|
+
|
|
202
|
+
The node default entry is unchanged and keeps its synchronous `node:crypto` fast
|
|
203
|
+
paths. This subpath is purely additive. Added in 0.3.0; earlier versions export
|
|
204
|
+
only `.` and `./canonicalize`.
|
|
205
|
+
|
|
206
|
+
## Authority verification
|
|
207
|
+
|
|
208
|
+
Capability-grant and delegation-chain verification is NOT in this package. It is
|
|
209
|
+
authorization policy, not object identity, and it moved to
|
|
210
|
+
[`@synoi/authority-verify`](https://github.com/synoi/synoi-authority-verify) in
|
|
211
|
+
0.4.0.
|
|
212
|
+
|
|
213
|
+
`capabilityCovers`, `AuthorityResolver` and `GrantStatus` did NOT move - they are
|
|
214
|
+
a pure string predicate and a live-status contract, and remain in the surface
|
|
215
|
+
above.
|
|
216
|
+
|
|
116
217
|
## Canonical form
|
|
117
218
|
|
|
118
|
-
The canonical form is
|
|
219
|
+
The canonical form is a strict RFC 8785 (JCS) profile, tested against the
|
|
220
|
+
RFC 8785 vectors (`test/rfc8785-conformance.test.ts`):
|
|
119
221
|
|
|
120
222
|
- primitives via `JSON.stringify`,
|
|
121
223
|
- objects emit keys in lexicographic order,
|
|
@@ -125,7 +227,7 @@ The canonical form is recursive JCS-lite JSON:
|
|
|
125
227
|
|
|
126
228
|
This canonical form is a wire contract. **Any byte-level change to the
|
|
127
229
|
serializer changes every OID and invalidates every previously-created
|
|
128
|
-
signature.** Treat it as frozen for SRAID
|
|
230
|
+
signature.** Treat it as frozen for SRAID v2.0; evolve it only via a new,
|
|
129
231
|
explicitly versioned canonical profile.
|
|
130
232
|
|
|
131
233
|
## OID format
|
|
@@ -134,8 +236,12 @@ explicitly versioned canonical profile.
|
|
|
134
236
|
OID = "sha256:" + lowercase_hex( sha256( canonicalize(content) ) )
|
|
135
237
|
```
|
|
136
238
|
|
|
137
|
-
The hash input is the
|
|
138
|
-
signature
|
|
239
|
+
The hash input is the object MINUS the six detached envelope fields
|
|
240
|
+
(`oid`, `signature`, `ml_dsa_signature`, `signature_key_id`,
|
|
241
|
+
`signature_algorithm`, `attestation`) — see `CDRO_ENVELOPE_FIELDS`. Everything
|
|
242
|
+
else is hashed, including `authority`, `sensitivity`, `prev`, `links` and
|
|
243
|
+
`supersedes`, so those are identity-bound and cannot be silently stripped.
|
|
244
|
+
Signing or rotating a signature never changes the OID.
|
|
139
245
|
|
|
140
246
|
## Why hybrid signatures
|
|
141
247
|
|
|
@@ -146,4 +252,4 @@ in this package returns `valid: true` only when BOTH succeed.
|
|
|
146
252
|
|
|
147
253
|
## License
|
|
148
254
|
|
|
149
|
-
MIT
|
|
255
|
+
MIT, see [`LICENSE`](./LICENSE).
|
package/SPEC.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SRAID -
|
|
1
|
+
# SRAID - Core Object Specification
|
|
2
2
|
|
|
3
3
|
**Core Specification, Version 1.0**
|
|
4
4
|
|
|
@@ -38,7 +38,7 @@ A SRAID object (the Canonical Data Record Object, or **CDRO**) is a map with the
|
|
|
38
38
|
|
|
39
39
|
The `oid`, `attestation`, and `signature` members are excluded from the OID input (Section 4) so that the same content produces the same OID and re-signing never changes identity. All other members are part of the canonical content and therefore contribute to the OID.
|
|
40
40
|
|
|
41
|
-
**Lineage fields are independent.** An object MAY carry `supersedes`, `prev`, and one or more `links[]` simultaneously. `supersedes` asserts a version-replacement relationship (this object is the authoritative successor of the referenced OID); `prev` is the hash-chain backpointer for ordered append; `links[]` are typed semantic edges (L3). Each is validated for canonical-OID format in isolation. The validator does NOT enforce agreement among them (for example it does not require `supersedes === prev`), nor does it resolve or fetch the referenced objects. Higher layers (Vault/Resolver, GAP) are responsible for any cross-field lineage semantics. The
|
|
41
|
+
**Lineage fields are independent.** An object MAY carry `supersedes`, `prev`, and one or more `links[]` simultaneously. `supersedes` asserts a version-replacement relationship (this object is the authoritative successor of the referenced OID); `prev` is the hash-chain backpointer for ordered append; `links[]` are typed semantic edges (L3). Each is validated for canonical-OID format in isolation. The validator does NOT enforce agreement among them (for example it does not require `supersedes === prev`), nor does it resolve or fetch the referenced objects. Higher layers (Vault/Resolver, GAP) are responsible for any cross-field lineage semantics. The standalone supersession receipt (SRO, type `sraid:sro`) was REMOVED in 0.4.0: nothing in any language consumed it, `lineage.ts` never handled it, and the canon had already retired the term (ADR_022 section 0). A caller recording WHY something was superseded should put that in the successor's own `body`, where it is hashed and signed.
|
|
42
42
|
|
|
43
43
|
## 3. Canonical form
|
|
44
44
|
|
package/dist/attestation.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
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
|
-
|
|
55
|
-
export
|
|
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
|
|
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"}
|
package/dist/attestation.js
CHANGED
|
@@ -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
|
-
|
|
38
|
-
export
|
|
39
|
-
|
|
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
|
|
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
|
-
//
|
|
151
|
-
//
|
|
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
|
}
|
package/dist/attestation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attestation.js","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"attestation.js","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @synoi/sraid — capability.ts
|
|
3
|
+
*
|
|
4
|
+
* Capability-pattern matching and the live-status contract.
|
|
5
|
+
*
|
|
6
|
+
* These stay in the core entry, while the grant/delegation VERIFIERS moved to
|
|
7
|
+
* `@synoi/sraid/authority` in 0.4.0, because they are different things:
|
|
8
|
+
*
|
|
9
|
+
* - `capabilityCovers` is a pure, total string predicate with no crypto, no
|
|
10
|
+
* I/O and no policy. It decides whether one dotted-taxonomy pattern covers
|
|
11
|
+
* another, and it is the leaf primitive that grant stores build on
|
|
12
|
+
* (synoi-app's `in-memory-engine.ts` and `app-store/store.ts` both call it
|
|
13
|
+
* directly rather than going through a verifier).
|
|
14
|
+
* - `GrantStatus` / `AuthorityResolver` are the CONTRACT for live grant
|
|
15
|
+
* state. The contract is a type; satisfying it is somebody else's job.
|
|
16
|
+
* Keeping it here lets a store, a resolver and a verifier agree on shape
|
|
17
|
+
* without any of them depending on the verifier.
|
|
18
|
+
*
|
|
19
|
+
* Nothing in this module makes, or can make, a live claim.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Match a capability `target` against a grant `pattern`. Pure string logic,
|
|
23
|
+
* re-stated here so the core stays dependency-free (the same rule lives in
|
|
24
|
+
* `@synoi/gap-types` `capabilityMatches`; the object layer must not depend on
|
|
25
|
+
* the protocol layer).
|
|
26
|
+
*
|
|
27
|
+
* - exact match → true
|
|
28
|
+
* - '*' → match-all
|
|
29
|
+
* - 'skill.*' matches 'skill.create' and deeper (segment-boundary only).
|
|
30
|
+
* A non-boundary 'admin.us*' must NOT match 'admin.users.delete'
|
|
31
|
+
* (privilege-escalation footgun) — only a '.'-anchored '*' is a wildcard.
|
|
32
|
+
*/
|
|
33
|
+
export declare function capabilityCovers(pattern: string, target: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The live-state interface that the OID Resolver implements. It is the ONLY
|
|
36
|
+
* source of truth for the two properties that cannot be checked offline:
|
|
37
|
+
* whether a grant currently exists (resolves) and whether it has been revoked.
|
|
38
|
+
*
|
|
39
|
+
* RESOLVER-DEPENDENT: undeployed today (SRAID_FOUNDATION_PUNCHLIST C). Defined
|
|
40
|
+
* here so callers, a future resolver, and the out-of-core verifier all agree
|
|
41
|
+
* on one contract.
|
|
42
|
+
*/
|
|
43
|
+
export interface AuthorityResolver {
|
|
44
|
+
/**
|
|
45
|
+
* Resolve a grant OID to its current status. Implementations should return
|
|
46
|
+
* `{ exists: false }` for an unknown OID and `{ exists: true, revoked: true,
|
|
47
|
+
* revoked_at_ms }` for a revoked one.
|
|
48
|
+
*/
|
|
49
|
+
resolveGrantStatus(grantOid: string): Promise<GrantStatus> | GrantStatus;
|
|
50
|
+
}
|
|
51
|
+
export interface GrantStatus {
|
|
52
|
+
/** Whether the grant OID resolves to a known, published grant. */
|
|
53
|
+
exists: boolean;
|
|
54
|
+
/** Whether the grant has been revoked. Only meaningful when `exists`. */
|
|
55
|
+
revoked?: boolean;
|
|
56
|
+
/** When the revocation took effect, if revoked. */
|
|
57
|
+
revoked_at_ms?: number;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=capability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAQzE;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAAA;CACzE;AAED,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,MAAM,EAAE,OAAO,CAAA;IACf,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,mDAAmD;IACnD,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @synoi/sraid — capability.ts
|
|
3
|
+
*
|
|
4
|
+
* Capability-pattern matching and the live-status contract.
|
|
5
|
+
*
|
|
6
|
+
* These stay in the core entry, while the grant/delegation VERIFIERS moved to
|
|
7
|
+
* `@synoi/sraid/authority` in 0.4.0, because they are different things:
|
|
8
|
+
*
|
|
9
|
+
* - `capabilityCovers` is a pure, total string predicate with no crypto, no
|
|
10
|
+
* I/O and no policy. It decides whether one dotted-taxonomy pattern covers
|
|
11
|
+
* another, and it is the leaf primitive that grant stores build on
|
|
12
|
+
* (synoi-app's `in-memory-engine.ts` and `app-store/store.ts` both call it
|
|
13
|
+
* directly rather than going through a verifier).
|
|
14
|
+
* - `GrantStatus` / `AuthorityResolver` are the CONTRACT for live grant
|
|
15
|
+
* state. The contract is a type; satisfying it is somebody else's job.
|
|
16
|
+
* Keeping it here lets a store, a resolver and a verifier agree on shape
|
|
17
|
+
* without any of them depending on the verifier.
|
|
18
|
+
*
|
|
19
|
+
* Nothing in this module makes, or can make, a live claim.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Match a capability `target` against a grant `pattern`. Pure string logic,
|
|
23
|
+
* re-stated here so the core stays dependency-free (the same rule lives in
|
|
24
|
+
* `@synoi/gap-types` `capabilityMatches`; the object layer must not depend on
|
|
25
|
+
* the protocol layer).
|
|
26
|
+
*
|
|
27
|
+
* - exact match → true
|
|
28
|
+
* - '*' → match-all
|
|
29
|
+
* - 'skill.*' matches 'skill.create' and deeper (segment-boundary only).
|
|
30
|
+
* A non-boundary 'admin.us*' must NOT match 'admin.users.delete'
|
|
31
|
+
* (privilege-escalation footgun) — only a '.'-anchored '*' is a wildcard.
|
|
32
|
+
*/
|
|
33
|
+
export function capabilityCovers(pattern, target) {
|
|
34
|
+
if (pattern === target)
|
|
35
|
+
return true;
|
|
36
|
+
if (pattern === '*')
|
|
37
|
+
return true;
|
|
38
|
+
if (pattern.endsWith('.*')) {
|
|
39
|
+
const prefix = pattern.slice(0, -1); // keep trailing '.', e.g. 'skill.'
|
|
40
|
+
return target.startsWith(prefix);
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=capability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability.js","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe,EAAE,MAAc;IAC9D,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,IAAI,CAAA;IACnC,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,IAAI,CAAA;IAChC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA,CAAC,mCAAmC;QACvE,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;IAClC,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @synoi/sraid — public surface.
|
|
3
3
|
*
|
|
4
|
-
* SRAID
|
|
4
|
+
* SRAID is the object-identity layer: it defines what a signed object IS and
|
|
5
|
+
* how anyone re-derives and verifies its identity offline.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* L4 Apps
|
|
7
|
+
* Scope, precisely:
|
|
8
|
+
* IN canonical serializer, OID computation, hybrid Ed25519 + ML-DSA-65
|
|
9
|
+
* attestation verification, lineage resolution, shape validators.
|
|
10
|
+
* OUT storage, transport, key resolution, revocation, policy, enforcement.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* It verifies SIGNATURES, not IDENTITIES: `signer_kid` is an opaque string
|
|
13
|
+
* this package never resolves, and nothing here checks revocation. Deciding
|
|
14
|
+
* whether a key was trusted at signing time is a caller/directory concern.
|
|
15
15
|
*/
|
|
16
|
-
export type { AttestationEnvelope, AttestationSignature, AuthorityBlock, AuthorityDecision, CDRO, LineageLink, LinkRel,
|
|
16
|
+
export type { AttestationEnvelope, AttestationSignature, AuthorityBlock, AuthorityDecision, CDRO, LineageLink, LinkRel, SensitivityTier, SignatureEnvelope, } from './types.js';
|
|
17
17
|
export { canonicalize } from './canonicalize.js';
|
|
18
18
|
export { oidOf, oidOfCanonical, cdroOid, cdroContentCore, CDRO_ENVELOPE_FIELDS } from './oid.js';
|
|
19
19
|
export { verifySignature, type VerifySignatureInput, type VerifySignatureResult, } from './signature.js';
|
|
@@ -22,7 +22,7 @@ export { evictKeyFromCaches } from './internal/key-cache.js';
|
|
|
22
22
|
export { verifyAttestation, pae, ALG_ED25519, ALG_ML_DSA_65, type VerifyAttestationInput, type VerifyAttestationResult, } from './attestation.js';
|
|
23
23
|
export { lineageLinks, supersededOids, latestWins, type LatestWinsResult, } from './lineage.js';
|
|
24
24
|
export { SENSITIVITY_TIERS, SENSITIVITY_DEFAULT, isSensitivityTier, sensitivityRank, sensitivityMax, sensitivityCarryForward, sensitivityMonotoneCheck, } from './sensitivity.js';
|
|
25
|
-
export {
|
|
26
|
-
export
|
|
27
|
-
export { validateCdro,
|
|
25
|
+
export { capabilityCovers } from './capability.js';
|
|
26
|
+
export type { AuthorityResolver, GrantStatus } from './capability.js';
|
|
27
|
+
export { validateCdro, validateSignatureEnvelope, validateAttestationEnvelope, validateAuthorityBlock, validateLineageLink, type ValidationResult, } from './validate.js';
|
|
28
28
|
//# sourceMappingURL=index.d.ts.map
|