@sudp-protocol/authorizer 0.1.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 ADDED
@@ -0,0 +1,93 @@
1
+ # @sudp-protocol/authorizer
2
+
3
+ > Authorizer-side primitives for **SUDP** — the Secret-Use Delegation Protocol.
4
+
5
+ The Authorizer is the party that authorizes a secret-backed operation by
6
+ signing the binding hash `β = SHA-256(DS_BIND ‖ r ‖ H(canonical(o)))`
7
+ with an authenticator. This package ships the carrier-agnostic crypto the
8
+ Authorizer needs, plus an optional WebAuthn adapter.
9
+
10
+ The Rust counterpart that runs in the Custodian is the
11
+ [`sudp` crate](../../custodian/rust/) in the same repository. Both sides
12
+ must agree byte-for-byte on canonical encoding, β, the AEAD-as-wrap AAD
13
+ shapes, and the wrap-key derivation.
14
+
15
+ ## Layout
16
+
17
+ ```
18
+ @sudp-protocol/authorizer ← carrier-agnostic protocol primitives
19
+ canonicalize, sha256, computeBinding,
20
+ deriveWrappingKey, wrapBindingAd, sealAd,
21
+ aeadSeal, aeadOpen, base64url helpers,
22
+ DS_BIND / DS_WRAP / DS_SEAL constants
23
+
24
+ @sudp-protocol/authorizer/webauthn ← WebAuthn-specific adapter
25
+ prfToUserKey(prfOutput) → 32-byte y_c
26
+ assertionToWire(assertion) → wire-shape assertion
27
+ ```
28
+
29
+ Other authenticator realisations (YubiKey static, secure-enclave, HSM,
30
+ mock) bring their own adapters and do **not** touch the WebAuthn subpath.
31
+ The core remains agnostic of how `y_c` was produced.
32
+
33
+ ## Usage sketch
34
+
35
+ ```ts
36
+ import {
37
+ computeBinding,
38
+ DS_BIND,
39
+ deriveWrappingKey,
40
+ wrapBindingAd,
41
+ aeadSeal,
42
+ } from "@sudp-protocol/authorizer";
43
+ import { prfToUserKey, assertionToWire } from "@sudp-protocol/authorizer/webauthn";
44
+
45
+ // 1) Authorizer-side: compute the binding hash β.
46
+ const beta = await computeBinding(DS_BIND, rFreshness, operation);
47
+
48
+ // 2) Run the WebAuthn ceremony with `beta` as the challenge. The PRF
49
+ // extension returns raw bytes — turn them into the 32-byte y_c.
50
+ const cred = (await navigator.credentials.get({
51
+ publicKey: {
52
+ challenge: beta,
53
+ extensions: { prf: { eval: { first: prfSalt } } },
54
+ /* ... rpId, allowCredentials, userVerification: "required", etc. */
55
+ } as PublicKeyCredentialRequestOptions,
56
+ })) as PublicKeyCredential;
57
+
58
+ const prfOut = new Uint8Array(
59
+ (cred.getClientExtensionResults() as { prf?: { results?: { first?: ArrayBuffer } } })
60
+ .prf!.results!.first!,
61
+ );
62
+ const yc = await prfToUserKey(prfOut);
63
+
64
+ // 3) Derive W_c and use it to wrap key material destined for the custodian.
65
+ const Wc = await deriveWrappingKey(yc, prfSalt, credentialId);
66
+ const wrapped = aeadSeal(Wc, plaintext, wrapBindingAd(credentialId));
67
+
68
+ // 4) Ship `{ assertionToWire(cred), wrapped, ... }` to the custodian as
69
+ // part of the grant.
70
+ ```
71
+
72
+ ## End-to-end protocol walkthrough
73
+
74
+ For how this package fits with the Rust Custodian and the Requester
75
+ across all three phases:
76
+
77
+ - Runnable, three-process demo over HTTP:
78
+ [`../../examples/protocol-demo/`](../../examples/protocol-demo/).
79
+ - Byte-level conformance map: the [top-level
80
+ README](../../README.md#cross-language-alignment).
81
+ - Authorizer-side walkthrough as a conformance test:
82
+ [`test/protocol_flow.test.ts`](test/protocol_flow.test.ts).
83
+
84
+ ## Status
85
+
86
+ Pre-1.0, alongside the Rust crate. Wire format and trait shapes may move
87
+ before the 1.0 cut. See the
88
+ [top-level README](https://github.com/xhyumiracle/sudp) and the
89
+ [Rust crate's CHANGELOG](../../custodian/rust/CHANGELOG.md).
90
+
91
+ ## License
92
+
93
+ Apache-2.0. See [LICENSE](../../LICENSE).
package/dist/aad.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ export declare const DS_WRAP: Uint8Array<ArrayBufferLike>;
2
+ export declare const DS_SEAL: Uint8Array<ArrayBufferLike>;
3
+ /**
4
+ * Current wrap version. Bumped together on both ends of the protocol if the
5
+ * AAD shape ever changes.
6
+ */
7
+ export declare const WRAP_VERSION = 1;
8
+ /**
9
+ * Canonical AAD for the AEAD-as-wrap profile:
10
+ *
11
+ * DS_WRAP ‖ credentialId ‖ ver_be(u16, big-endian)
12
+ *
13
+ * Bound as associated data when sealing/opening `K̂_c` under `W_c`, so a
14
+ * peer-wrapped record cannot be substituted across credentials or versions.
15
+ */
16
+ export declare function wrapBindingAd(credentialId: Uint8Array, wrapVersion?: number): Uint8Array;
17
+ /**
18
+ * Canonical AAD for the sealed-body AEAD layer:
19
+ *
20
+ * DS_SEAL ‖ ver_be(u16, big-endian)
21
+ */
22
+ export declare function sealAd(wrapVersion?: number): Uint8Array;
23
+ //# sourceMappingURL=aad.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aad.d.ts","sourceRoot":"","sources":["../src/aad.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,6BAAuB,CAAC;AAC5C,eAAO,MAAM,OAAO,6BAAuB,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,YAAY,IAAS,CAAC;AAEnC;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,YAAY,EAAE,UAAU,EACxB,WAAW,GAAE,MAAqB,GACjC,UAAU,CAEZ;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,WAAW,GAAE,MAAqB,GAAG,UAAU,CAErE"}
package/dist/aad.js ADDED
@@ -0,0 +1,28 @@
1
+ import { concatBytes, u16beBytes, utf8 } from "./bytes.js";
2
+ export const DS_WRAP = utf8("sudp/v1/wrap");
3
+ export const DS_SEAL = utf8("sudp/v1/seal");
4
+ /**
5
+ * Current wrap version. Bumped together on both ends of the protocol if the
6
+ * AAD shape ever changes.
7
+ */
8
+ export const WRAP_VERSION = 0x0001;
9
+ /**
10
+ * Canonical AAD for the AEAD-as-wrap profile:
11
+ *
12
+ * DS_WRAP ‖ credentialId ‖ ver_be(u16, big-endian)
13
+ *
14
+ * Bound as associated data when sealing/opening `K̂_c` under `W_c`, so a
15
+ * peer-wrapped record cannot be substituted across credentials or versions.
16
+ */
17
+ export function wrapBindingAd(credentialId, wrapVersion = WRAP_VERSION) {
18
+ return concatBytes(DS_WRAP, credentialId, u16beBytes(wrapVersion));
19
+ }
20
+ /**
21
+ * Canonical AAD for the sealed-body AEAD layer:
22
+ *
23
+ * DS_SEAL ‖ ver_be(u16, big-endian)
24
+ */
25
+ export function sealAd(wrapVersion = WRAP_VERSION) {
26
+ return concatBytes(DS_SEAL, u16beBytes(wrapVersion));
27
+ }
28
+ //# sourceMappingURL=aad.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aad.js","sourceRoot":"","sources":["../src/aad.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;AAC5C,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;AAE5C;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC;AAEnC;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,YAAwB,EACxB,cAAsB,YAAY;IAElC,OAAO,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;AACrE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,cAAsB,YAAY;IACvD,OAAO,WAAW,CAAC,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;AACvD,CAAC"}
package/dist/aead.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * XChaCha20-Poly1305 raw encrypt with a caller-supplied nonce.
3
+ *
4
+ * Output is `ciphertext ‖ tag` (no nonce prefix). Use {@link aeadSeal} for
5
+ * the standard SUDP wire format that prepends a freshly random nonce.
6
+ *
7
+ * MUST stay byte-for-byte aligned with the Rust crate's
8
+ * `sudp::primitives::Aead::encrypt`.
9
+ */
10
+ export declare function aeadEncrypt(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, aad: Uint8Array): Uint8Array;
11
+ /**
12
+ * XChaCha20-Poly1305 thin wrapper.
13
+ *
14
+ * Wire layout (MUST match `sudp::primitives::Aead::seal`):
15
+ *
16
+ * nonce(24 bytes) ‖ ciphertext ‖ tag(16 bytes)
17
+ *
18
+ * The 24-byte nonce is freshly generated per call. The caller supplies the
19
+ * canonical AAD (see {@link wrapBindingAd}, {@link sealAd}).
20
+ */
21
+ export declare function aeadSeal(key: Uint8Array, plaintext: Uint8Array, aad: Uint8Array): Uint8Array;
22
+ /**
23
+ * Counterpart to {@link aeadSeal}. Throws if the AAD/nonce/ciphertext are
24
+ * not authentic under `key`.
25
+ */
26
+ export declare function aeadOpen(key: Uint8Array, sealed: Uint8Array, aad: Uint8Array): Uint8Array;
27
+ //# sourceMappingURL=aead.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aead.d.ts","sourceRoot":"","sources":["../src/aead.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,UAAU,EACrB,GAAG,EAAE,UAAU,GACd,UAAU,CAKZ;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,UAAU,EACrB,GAAG,EAAE,UAAU,GACd,UAAU,CAOZ;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,UAAU,GACd,UAAU,CAOZ"}
package/dist/aead.js ADDED
@@ -0,0 +1,49 @@
1
+ import { xchacha20poly1305 } from "@noble/ciphers/chacha.js";
2
+ const XNONCE_LEN = 24;
3
+ const XTAG_LEN = 16;
4
+ /**
5
+ * XChaCha20-Poly1305 raw encrypt with a caller-supplied nonce.
6
+ *
7
+ * Output is `ciphertext ‖ tag` (no nonce prefix). Use {@link aeadSeal} for
8
+ * the standard SUDP wire format that prepends a freshly random nonce.
9
+ *
10
+ * MUST stay byte-for-byte aligned with the Rust crate's
11
+ * `sudp::primitives::Aead::encrypt`.
12
+ */
13
+ export function aeadEncrypt(key, nonce, plaintext, aad) {
14
+ if (nonce.byteLength !== XNONCE_LEN) {
15
+ throw new Error(`aeadEncrypt: nonce must be ${XNONCE_LEN} bytes, got ${nonce.byteLength}`);
16
+ }
17
+ return xchacha20poly1305(key, nonce, aad).encrypt(plaintext);
18
+ }
19
+ /**
20
+ * XChaCha20-Poly1305 thin wrapper.
21
+ *
22
+ * Wire layout (MUST match `sudp::primitives::Aead::seal`):
23
+ *
24
+ * nonce(24 bytes) ‖ ciphertext ‖ tag(16 bytes)
25
+ *
26
+ * The 24-byte nonce is freshly generated per call. The caller supplies the
27
+ * canonical AAD (see {@link wrapBindingAd}, {@link sealAd}).
28
+ */
29
+ export function aeadSeal(key, plaintext, aad) {
30
+ const nonce = crypto.getRandomValues(new Uint8Array(XNONCE_LEN));
31
+ const ct = aeadEncrypt(key, nonce, plaintext, aad);
32
+ const out = new Uint8Array(XNONCE_LEN + ct.byteLength);
33
+ out.set(nonce, 0);
34
+ out.set(ct, XNONCE_LEN);
35
+ return out;
36
+ }
37
+ /**
38
+ * Counterpart to {@link aeadSeal}. Throws if the AAD/nonce/ciphertext are
39
+ * not authentic under `key`.
40
+ */
41
+ export function aeadOpen(key, sealed, aad) {
42
+ if (sealed.byteLength <= XNONCE_LEN + XTAG_LEN) {
43
+ throw new Error("aeadOpen: sealed blob is too short");
44
+ }
45
+ const nonce = sealed.slice(0, XNONCE_LEN);
46
+ const ct = sealed.slice(XNONCE_LEN);
47
+ return xchacha20poly1305(key, nonce, aad).decrypt(ct);
48
+ }
49
+ //# sourceMappingURL=aead.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aead.js","sourceRoot":"","sources":["../src/aead.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,QAAQ,GAAG,EAAE,CAAC;AAEpB;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CACzB,GAAe,EACf,KAAiB,EACjB,SAAqB,EACrB,GAAe;IAEf,IAAI,KAAK,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,8BAA8B,UAAU,eAAe,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CACtB,GAAe,EACf,SAAqB,EACrB,GAAe;IAEf,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IACjE,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,UAAU,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC;IACvD,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAClB,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IACxB,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CACtB,GAAe,EACf,MAAkB,EAClB,GAAe;IAEf,IAAI,MAAM,CAAC,UAAU,IAAI,UAAU,GAAG,QAAQ,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAC1C,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACpC,OAAO,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Domain separation tag for the Phase II.2 binding hash. Matches the Rust
3
+ * crate's `sudp::primitives::domain::DS_BIND` byte-for-byte.
4
+ *
5
+ * β = SHA-256(DS_BIND ‖ r ‖ SHA-256(canonical(o)))
6
+ */
7
+ export declare const DS_BIND: Uint8Array<ArrayBufferLike>;
8
+ /**
9
+ * Compute the binding hash `β` for a given operation, freshness `r`, and
10
+ * domain separation tag.
11
+ *
12
+ * β = SHA-256(domain ‖ r ‖ SHA-256(canonical(op)))
13
+ *
14
+ * The Authorizer signs `β` with its authenticator. The custodian recomputes
15
+ * `β` from the redeemed grant's `(o, r)` and verifies the signature.
16
+ *
17
+ * Pass {@link DS_BIND} for the default profile; other domains may be used
18
+ * by adjacent ceremonies (e.g. setup attestation) and live in the
19
+ * deployment.
20
+ */
21
+ export declare function computeBinding(domain: Uint8Array, r: Uint8Array, op: unknown): Promise<Uint8Array>;
22
+ //# sourceMappingURL=binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,eAAO,MAAM,OAAO,6BAAuB,CAAC;AAE5C;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,CAAC,EAAE,UAAU,EACb,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,UAAU,CAAC,CAGrB"}
@@ -0,0 +1,28 @@
1
+ import { concatBytes, utf8 } from "./bytes.js";
2
+ import { canonicalize } from "./canonical.js";
3
+ import { sha256 } from "./hash.js";
4
+ /**
5
+ * Domain separation tag for the Phase II.2 binding hash. Matches the Rust
6
+ * crate's `sudp::primitives::domain::DS_BIND` byte-for-byte.
7
+ *
8
+ * β = SHA-256(DS_BIND ‖ r ‖ SHA-256(canonical(o)))
9
+ */
10
+ export const DS_BIND = utf8("sudp/v1/bind");
11
+ /**
12
+ * Compute the binding hash `β` for a given operation, freshness `r`, and
13
+ * domain separation tag.
14
+ *
15
+ * β = SHA-256(domain ‖ r ‖ SHA-256(canonical(op)))
16
+ *
17
+ * The Authorizer signs `β` with its authenticator. The custodian recomputes
18
+ * `β` from the redeemed grant's `(o, r)` and verifies the signature.
19
+ *
20
+ * Pass {@link DS_BIND} for the default profile; other domains may be used
21
+ * by adjacent ceremonies (e.g. setup attestation) and live in the
22
+ * deployment.
23
+ */
24
+ export async function computeBinding(domain, r, op) {
25
+ const opHash = await sha256(canonicalize(op));
26
+ return sha256(concatBytes(domain, r, opHash));
27
+ }
28
+ //# sourceMappingURL=binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding.js","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;AAE5C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAkB,EAClB,CAAa,EACb,EAAW;IAEX,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9C,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare function utf8(s: string): Uint8Array;
2
+ export declare function concatBytes(...parts: readonly Uint8Array[]): Uint8Array;
3
+ export declare function u16beBytes(n: number): Uint8Array;
4
+ export declare function bytesToB64Url(b: Uint8Array): string;
5
+ export declare function b64UrlToBytes(s: string): Uint8Array;
6
+ //# sourceMappingURL=bytes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../src/bytes.ts"],"names":[],"mappings":"AAEA,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAE1C;AAED,wBAAgB,WAAW,CAAC,GAAG,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,UAAU,CAUvE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAKhD;AAcD,wBAAgB,aAAa,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAgBnD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAiCnD"}
package/dist/bytes.js ADDED
@@ -0,0 +1,91 @@
1
+ const enc = new TextEncoder();
2
+ export function utf8(s) {
3
+ return enc.encode(s);
4
+ }
5
+ export function concatBytes(...parts) {
6
+ let total = 0;
7
+ for (const p of parts)
8
+ total += p.byteLength;
9
+ const out = new Uint8Array(total);
10
+ let off = 0;
11
+ for (const p of parts) {
12
+ out.set(p, off);
13
+ off += p.byteLength;
14
+ }
15
+ return out;
16
+ }
17
+ export function u16beBytes(n) {
18
+ if (!Number.isInteger(n) || n < 0 || n > 0xffff) {
19
+ throw new Error(`u16beBytes: out of range: ${n}`);
20
+ }
21
+ return new Uint8Array([(n >> 8) & 0xff, n & 0xff]);
22
+ }
23
+ const B64URL_ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
24
+ const B64URL_LOOKUP = (() => {
25
+ const t = new Int8Array(256).fill(-1);
26
+ for (let i = 0; i < B64URL_ALPHA.length; i++)
27
+ t[B64URL_ALPHA.charCodeAt(i)] = i;
28
+ return t;
29
+ })();
30
+ function alpha(i) {
31
+ // Index is always masked to 0..63, so B64URL_ALPHA[i] is defined.
32
+ return B64URL_ALPHA[i];
33
+ }
34
+ export function bytesToB64Url(b) {
35
+ let out = "";
36
+ let i = 0;
37
+ for (; i + 3 <= b.length; i += 3) {
38
+ const x = (b[i] << 16) | (b[i + 1] << 8) | b[i + 2];
39
+ out += alpha((x >> 18) & 0x3f) + alpha((x >> 12) & 0x3f) + alpha((x >> 6) & 0x3f) + alpha(x & 0x3f);
40
+ }
41
+ const rem = b.length - i;
42
+ if (rem === 1) {
43
+ const x = b[i] << 16;
44
+ out += alpha((x >> 18) & 0x3f) + alpha((x >> 12) & 0x3f);
45
+ }
46
+ else if (rem === 2) {
47
+ const x = (b[i] << 16) | (b[i + 1] << 8);
48
+ out += alpha((x >> 18) & 0x3f) + alpha((x >> 12) & 0x3f) + alpha((x >> 6) & 0x3f);
49
+ }
50
+ return out;
51
+ }
52
+ export function b64UrlToBytes(s) {
53
+ const norm = s.replace(/=+$/, "");
54
+ const len = norm.length;
55
+ const fullGroups = Math.floor(len / 4);
56
+ const rem = len - fullGroups * 4;
57
+ if (rem === 1)
58
+ throw new Error("b64UrlToBytes: invalid length");
59
+ const out = new Uint8Array(fullGroups * 3 + (rem === 0 ? 0 : rem - 1));
60
+ let outOff = 0;
61
+ let i = 0;
62
+ for (; i + 4 <= len; i += 4) {
63
+ const a = B64URL_LOOKUP[norm.charCodeAt(i)];
64
+ const b = B64URL_LOOKUP[norm.charCodeAt(i + 1)];
65
+ const c = B64URL_LOOKUP[norm.charCodeAt(i + 2)];
66
+ const d = B64URL_LOOKUP[norm.charCodeAt(i + 3)];
67
+ if ((a | b | c | d) < 0)
68
+ throw new Error("b64UrlToBytes: invalid character");
69
+ out[outOff++] = (a << 2) | (b >> 4);
70
+ out[outOff++] = ((b & 0x0f) << 4) | (c >> 2);
71
+ out[outOff++] = ((c & 0x03) << 6) | d;
72
+ }
73
+ if (rem === 2) {
74
+ const a = B64URL_LOOKUP[norm.charCodeAt(i)];
75
+ const b = B64URL_LOOKUP[norm.charCodeAt(i + 1)];
76
+ if ((a | b) < 0)
77
+ throw new Error("b64UrlToBytes: invalid character");
78
+ out[outOff++] = (a << 2) | (b >> 4);
79
+ }
80
+ else if (rem === 3) {
81
+ const a = B64URL_LOOKUP[norm.charCodeAt(i)];
82
+ const b = B64URL_LOOKUP[norm.charCodeAt(i + 1)];
83
+ const c = B64URL_LOOKUP[norm.charCodeAt(i + 2)];
84
+ if ((a | b | c) < 0)
85
+ throw new Error("b64UrlToBytes: invalid character");
86
+ out[outOff++] = (a << 2) | (b >> 4);
87
+ out[outOff++] = ((b & 0x0f) << 4) | (c >> 2);
88
+ }
89
+ return out;
90
+ }
91
+ //# sourceMappingURL=bytes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bytes.js","sourceRoot":"","sources":["../src/bytes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * JCS-style canonical JSON encoder (RFC 8785 subset).
3
+ *
4
+ * Properties:
5
+ * - Object keys are sorted lexicographically by UTF-16 code unit.
6
+ * - Strings use `JSON.stringify` escaping.
7
+ * - Numbers must be finite. Floats are rejected — they have no
8
+ * byte-reproducible canonical form across endpoints and would be a
9
+ * substitution vector against `H(o)`. Use integers, strings, booleans,
10
+ * nulls, arrays, or nested objects.
11
+ * - `undefined` is treated as `null`.
12
+ *
13
+ * This MUST stay byte-for-byte aligned with the Rust crate's
14
+ * `sudp::canonical::canonicalize_strict`. The `protocol/test_vectors/`
15
+ * directory carries the conformance suite.
16
+ */
17
+ export declare function canonicalize(value: unknown): Uint8Array;
18
+ //# sourceMappingURL=canonical.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.d.ts","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAEvD"}
@@ -0,0 +1,53 @@
1
+ import { utf8 } from "./bytes.js";
2
+ /**
3
+ * JCS-style canonical JSON encoder (RFC 8785 subset).
4
+ *
5
+ * Properties:
6
+ * - Object keys are sorted lexicographically by UTF-16 code unit.
7
+ * - Strings use `JSON.stringify` escaping.
8
+ * - Numbers must be finite. Floats are rejected — they have no
9
+ * byte-reproducible canonical form across endpoints and would be a
10
+ * substitution vector against `H(o)`. Use integers, strings, booleans,
11
+ * nulls, arrays, or nested objects.
12
+ * - `undefined` is treated as `null`.
13
+ *
14
+ * This MUST stay byte-for-byte aligned with the Rust crate's
15
+ * `sudp::canonical::canonicalize_strict`. The `protocol/test_vectors/`
16
+ * directory carries the conformance suite.
17
+ */
18
+ export function canonicalize(value) {
19
+ return utf8(canonicalizeStr(value));
20
+ }
21
+ function canonicalizeStr(v) {
22
+ if (v === null || v === undefined)
23
+ return "null";
24
+ if (typeof v === "boolean")
25
+ return v ? "true" : "false";
26
+ if (typeof v === "number") {
27
+ if (!Number.isFinite(v)) {
28
+ throw new Error("canonicalize: non-finite number is not allowed");
29
+ }
30
+ if (!Number.isInteger(v)) {
31
+ throw new Error("canonicalize: float values are rejected (no byte-reproducible canonical form)");
32
+ }
33
+ return v.toString();
34
+ }
35
+ if (typeof v === "bigint")
36
+ return v.toString();
37
+ if (typeof v === "string")
38
+ return JSON.stringify(v);
39
+ if (Array.isArray(v)) {
40
+ return "[" + v.map(canonicalizeStr).join(",") + "]";
41
+ }
42
+ if (typeof v === "object") {
43
+ const obj = v;
44
+ const keys = Object.keys(obj).sort();
45
+ return ("{" +
46
+ keys
47
+ .map((k) => JSON.stringify(k) + ":" + canonicalizeStr(obj[k]))
48
+ .join(",") +
49
+ "}");
50
+ }
51
+ throw new Error(`canonicalize: unsupported value type: ${typeof v}`);
52
+ }
53
+ //# sourceMappingURL=canonical.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.js","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,eAAe,CAAC,CAAU;IACjC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACjD,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IACxD,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IACtB,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/C,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACpD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACrB,OAAO,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACtD,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,CAA4B,CAAC;QACzC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,OAAO,CACL,GAAG;YACH,IAAI;iBACD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;iBAC7D,IAAI,CAAC,GAAG,CAAC;YACZ,GAAG,CACJ,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,yCAAyC,OAAO,CAAC,EAAE,CAAC,CAAC;AACvE,CAAC"}
package/dist/hash.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * SHA-256 over a byte buffer. Thin wrapper over the platform's WebCrypto.
3
+ */
4
+ export declare function sha256(data: Uint8Array): Promise<Uint8Array>;
5
+ //# sourceMappingURL=hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAIlE"}
package/dist/hash.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * SHA-256 over a byte buffer. Thin wrapper over the platform's WebCrypto.
3
+ */
4
+ export async function sha256(data) {
5
+ // `crypto.subtle` is available in modern browsers and Node >= 20.
6
+ const buf = await crypto.subtle.digest("SHA-256", data);
7
+ return new Uint8Array(buf);
8
+ }
9
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAgB;IAC3C,kEAAkE;IAClE,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAA8B,CAAC,CAAC;IAClF,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `@sudp-protocol/authorizer` — Authorizer-side primitives for the
3
+ * Secret-Use Delegation Protocol.
4
+ *
5
+ * This entry point is **carrier-agnostic**: it carries only the protocol
6
+ * cryptography (canonical JSON, β computation, wrapping-key derivation,
7
+ * AEAD-as-wrap) and intentionally does **not** know about WebAuthn,
8
+ * passkeys, HTTP, or any specific authenticator.
9
+ *
10
+ * For the WebAuthn PRF → y_c adapter and assertion helpers, import from
11
+ * `@sudp-protocol/authorizer/webauthn`.
12
+ */
13
+ export { utf8, concatBytes, u16beBytes, bytesToB64Url, b64UrlToBytes, } from "./bytes.js";
14
+ export { canonicalize } from "./canonical.js";
15
+ export { sha256 } from "./hash.js";
16
+ export { computeBinding, DS_BIND } from "./binding.js";
17
+ export { deriveWrappingKey } from "./kdf.js";
18
+ export { wrapBindingAd, sealAd, DS_WRAP, DS_SEAL, WRAP_VERSION } from "./aad.js";
19
+ export { aeadEncrypt, aeadSeal, aeadOpen } from "./aead.js";
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,IAAI,EACJ,WAAW,EACX,UAAU,EACV,aAAa,EACb,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `@sudp-protocol/authorizer` — Authorizer-side primitives for the
3
+ * Secret-Use Delegation Protocol.
4
+ *
5
+ * This entry point is **carrier-agnostic**: it carries only the protocol
6
+ * cryptography (canonical JSON, β computation, wrapping-key derivation,
7
+ * AEAD-as-wrap) and intentionally does **not** know about WebAuthn,
8
+ * passkeys, HTTP, or any specific authenticator.
9
+ *
10
+ * For the WebAuthn PRF → y_c adapter and assertion helpers, import from
11
+ * `@sudp-protocol/authorizer/webauthn`.
12
+ */
13
+ export { utf8, concatBytes, u16beBytes, bytesToB64Url, b64UrlToBytes, } from "./bytes.js";
14
+ export { canonicalize } from "./canonical.js";
15
+ export { sha256 } from "./hash.js";
16
+ export { computeBinding, DS_BIND } from "./binding.js";
17
+ export { deriveWrappingKey } from "./kdf.js";
18
+ export { wrapBindingAd, sealAd, DS_WRAP, DS_SEAL, WRAP_VERSION } from "./aad.js";
19
+ export { aeadEncrypt, aeadSeal, aeadOpen } from "./aead.js";
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,IAAI,EACJ,WAAW,EACX,UAAU,EACV,aAAa,EACb,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC"}
package/dist/kdf.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Derive the per-credential wrapping key `W_c` from the Authorizer's
3
+ * user-key `y_c` (a 32-byte secret produced by the authenticator).
4
+ *
5
+ * W_c = HKDF-SHA-256(y_c, salt = prf_salt, info = DS_WRAP ‖ credId ‖ ver_be)
6
+ *
7
+ * `y_c` must arrive at the Authorizer side already shaped to 32 bytes; how
8
+ * it is produced is authenticator-specific and outside the SUDP core (see
9
+ * `./webauthn` for the WebAuthn PRF → y_c adapter, but custom authenticators
10
+ * may provide y_c directly).
11
+ *
12
+ * MUST stay byte-for-byte aligned with the Rust crate's
13
+ * `sudp::crypto::kdf::derive_wrapping_key`.
14
+ */
15
+ export declare function deriveWrappingKey(userKey: Uint8Array, prfSalt: Uint8Array, credentialId: Uint8Array, wrapVersion?: number): Promise<Uint8Array>;
16
+ //# sourceMappingURL=kdf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kdf.d.ts","sourceRoot":"","sources":["../src/kdf.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,UAAU,EACnB,YAAY,EAAE,UAAU,EACxB,WAAW,GAAE,MAAqB,GACjC,OAAO,CAAC,UAAU,CAAC,CAoBrB"}
package/dist/kdf.js ADDED
@@ -0,0 +1,28 @@
1
+ import { concatBytes, u16beBytes } from "./bytes.js";
2
+ import { DS_WRAP, WRAP_VERSION } from "./aad.js";
3
+ /**
4
+ * Derive the per-credential wrapping key `W_c` from the Authorizer's
5
+ * user-key `y_c` (a 32-byte secret produced by the authenticator).
6
+ *
7
+ * W_c = HKDF-SHA-256(y_c, salt = prf_salt, info = DS_WRAP ‖ credId ‖ ver_be)
8
+ *
9
+ * `y_c` must arrive at the Authorizer side already shaped to 32 bytes; how
10
+ * it is produced is authenticator-specific and outside the SUDP core (see
11
+ * `./webauthn` for the WebAuthn PRF → y_c adapter, but custom authenticators
12
+ * may provide y_c directly).
13
+ *
14
+ * MUST stay byte-for-byte aligned with the Rust crate's
15
+ * `sudp::crypto::kdf::derive_wrapping_key`.
16
+ */
17
+ export async function deriveWrappingKey(userKey, prfSalt, credentialId, wrapVersion = WRAP_VERSION) {
18
+ const km = await crypto.subtle.importKey("raw", userKey, "HKDF", false, ["deriveBits"]);
19
+ const info = concatBytes(DS_WRAP, credentialId, u16beBytes(wrapVersion));
20
+ const bits = await crypto.subtle.deriveBits({
21
+ name: "HKDF",
22
+ hash: "SHA-256",
23
+ salt: prfSalt,
24
+ info: info,
25
+ }, km, 256);
26
+ return new Uint8Array(bits);
27
+ }
28
+ //# sourceMappingURL=kdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kdf.js","sourceRoot":"","sources":["../src/kdf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAmB,EACnB,OAAmB,EACnB,YAAwB,EACxB,cAAsB,YAAY;IAElC,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACtC,KAAK,EACL,OAAiC,EACjC,MAAM,EACN,KAAK,EACL,CAAC,YAAY,CAAC,CACf,CAAC;IACF,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;IACzE,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CACzC;QACE,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,OAAiC;QACvC,IAAI,EAAE,IAA8B;KACrC,EACD,EAAE,EACF,GAAG,CACJ,CAAC;IACF,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC"}