@cello-protocol/crypto 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/preauth-capability.d.ts +73 -0
- package/dist/preauth-capability.d.ts.map +1 -0
- package/dist/preauth-capability.js +117 -0
- package/dist/preauth-capability.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export { buildRelayRegistrationTbs, verifyRelayRegistrationSignature } from "./r
|
|
|
17
17
|
export { sealToRecipient, openSealed, CONTENT_SEAL_OVERHEAD_BYTES } from "./content-seal.js";
|
|
18
18
|
export type { ManifestVerifyResult, ManifestVerifyDiagnostics, ManifestVerifySkipReason, ManifestVerifySkippedEntry, ConsortiumManifestInput } from "./manifest.js";
|
|
19
19
|
export { canonicalManifestBody, verifyManifest } from "./manifest.js";
|
|
20
|
+
export type { PreAuthCapability, PreAuthCapabilityBody, CapabilityVerifyResult, CapabilityVerifyReason } from "./preauth-capability.js";
|
|
21
|
+
export { canonicalCapabilityBody, signCapability, verifyCapability, encodeCapability, decodeCapability } from "./preauth-capability.js";
|
|
20
22
|
export { CONSORTIUM_ROOT_KEYS, CONSORTIUM_THRESHOLD, TEST_CONSORTIUM_ROOT_KEYS, TEST_CONSORTIUM_THRESHOLD, } from "./consortium-keys.js";
|
|
21
23
|
export type { TestConsortiumNode, MakeTestManifestOpts } from "./manifest-test-fixture.js";
|
|
22
24
|
export { makeTestManifest, TEST_DIRECTORY_NODE_KEYPAIR } from "./manifest-test-fixture.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACpI,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAClG,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,WAAW,EACX,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC3F,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3F,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,0BAA0B,EAC1B,YAAY,EACZ,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,6BAA6B,EAC7B,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAIzE,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,YAAY,GACb,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,iCAAiC,EACjC,wBAAwB,GACzB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAGzD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG5E,OAAO,EAAE,yBAAyB,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAGtG,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAG7F,YAAY,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACpK,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACpI,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAClG,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,WAAW,EACX,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC3F,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3F,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,0BAA0B,EAC1B,YAAY,EACZ,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,6BAA6B,EAC7B,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAIzE,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,YAAY,GACb,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,iCAAiC,EACjC,wBAAwB,GACzB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAGzD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG5E,OAAO,EAAE,yBAAyB,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAGtG,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAG7F,YAAY,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACpK,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEtE,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACxI,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACxI,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ export { buildRelayRegistrationTbs, verifyRelayRegistrationSignature } from "./r
|
|
|
19
19
|
// M7-MSG-001: recipient content sealed box (E2E encryption for parked content)
|
|
20
20
|
export { sealToRecipient, openSealed, CONTENT_SEAL_OVERHEAD_BYTES } from "./content-seal.js";
|
|
21
21
|
export { canonicalManifestBody, verifyManifest } from "./manifest.js";
|
|
22
|
+
export { canonicalCapabilityBody, signCapability, verifyCapability, encodeCapability, decodeCapability } from "./preauth-capability.js";
|
|
22
23
|
export { CONSORTIUM_ROOT_KEYS, CONSORTIUM_THRESHOLD, TEST_CONSORTIUM_ROOT_KEYS, TEST_CONSORTIUM_THRESHOLD, } from "./consortium-keys.js";
|
|
23
24
|
export { makeTestManifest, TEST_DIRECTORY_NODE_KEYPAIR } from "./manifest-test-fixture.js";
|
|
24
25
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEpI,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,WAAW,EACX,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAE3F,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAU3F,OAAO,EACL,6BAA6B,EAC7B,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAE1B,iGAAiG;AACjG,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,uGAAuG;AACvG,0FAA0F;AAC1F,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,YAAY,GACb,MAAM,4BAA4B,CAAC;AAEpC,8FAA8F;AAC9F,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,iCAAiC,EACjC,wBAAwB,GACzB,MAAM,mCAAmC,CAAC;AAE3C,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,8EAA8E;AAC9E,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE5E,oEAAoE;AACpE,OAAO,EAAE,yBAAyB,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAEtG,+EAA+E;AAC/E,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAI7F,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEpI,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,WAAW,EACX,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAE3F,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAU3F,OAAO,EACL,6BAA6B,EAC7B,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAE1B,iGAAiG;AACjG,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,uGAAuG;AACvG,0FAA0F;AAC1F,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,YAAY,GACb,MAAM,4BAA4B,CAAC;AAEpC,8FAA8F;AAC9F,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,iCAAiC,EACjC,wBAAwB,GACzB,MAAM,mCAAmC,CAAC;AAE3C,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,8EAA8E;AAC9E,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE5E,oEAAoE;AACpE,OAAO,EAAE,yBAAyB,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAEtG,+EAA+E;AAC/E,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAI7F,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGtE,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACxI,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-authorization capability — M8B-PREAUTH-CAP.
|
|
3
|
+
*
|
|
4
|
+
* A signed permission slip authorizing exactly ONE agent registration. It replaces the opaque
|
|
5
|
+
* single-use pre-auth token: the token had to be looked up in the issuing directory's database, so
|
|
6
|
+
* during a T-of-N DKG the other sovereign nodes — which never hold the token (DOD-INV-2 forbids
|
|
7
|
+
* replicating bearer secrets) — returned NOT_FOUND and the ceremony failed. A capability carries an
|
|
8
|
+
* Ed25519 signature by a pinned issuer key, so EVERY directory verifies it independently, statelessly,
|
|
9
|
+
* with no DB lookup and nothing secret to replicate.
|
|
10
|
+
*
|
|
11
|
+
* Single-use is NOT enforced here — a signature is inherently replayable. It is enforced downstream by
|
|
12
|
+
* the directory binding the capability's `nonce` to the DKG epoch (one nonce ⇒ one agent). This module
|
|
13
|
+
* only mints and verifies the signed authorization and its validity window.
|
|
14
|
+
*
|
|
15
|
+
* Canonical body: all fields except `sig`, object keys sorted lexicographically at every level, JSON
|
|
16
|
+
* with no whitespace, UTF-8 encoded — the SAME scheme as canonicalManifestBody (manifest.ts), kept
|
|
17
|
+
* self-contained here so the two crypto primitives stay decoupled.
|
|
18
|
+
*
|
|
19
|
+
* Crypto reference: RFC 8032 (Ed25519).
|
|
20
|
+
*/
|
|
21
|
+
import type { KeyProvider } from "./types.js";
|
|
22
|
+
/** The signed fields of a pre-auth capability (everything except the signature). */
|
|
23
|
+
export interface PreAuthCapabilityBody {
|
|
24
|
+
/** Random 128-bit replay identity as 32 lowercase hex chars (16 bytes). NOT a secret; one ⇒ one agent. */
|
|
25
|
+
nonce: string;
|
|
26
|
+
/** SHA-256 of the phone stub (hex) — the same binding datum the token row carried (SI-001: never the raw stub). */
|
|
27
|
+
phone_stub_hash: string;
|
|
28
|
+
/** Email domain of the authorized account. */
|
|
29
|
+
email_domain: string;
|
|
30
|
+
/** ISO-8601 issue time (informational). */
|
|
31
|
+
issued_at: string;
|
|
32
|
+
/** ISO-8601 expiry — the capability is invalid at or after this instant. */
|
|
33
|
+
expires_at: string;
|
|
34
|
+
}
|
|
35
|
+
/** A signed pre-auth capability: the body plus the issuer's Ed25519 signature over its canonical bytes. */
|
|
36
|
+
export interface PreAuthCapability extends PreAuthCapabilityBody {
|
|
37
|
+
/** Ed25519 signature (128 hex chars = 64 bytes) over canonicalCapabilityBody, by the issuer key. */
|
|
38
|
+
sig: string;
|
|
39
|
+
}
|
|
40
|
+
export type CapabilityVerifyReason = "capability_malformed" | "capability_signature_invalid" | "capability_expired";
|
|
41
|
+
export type CapabilityVerifyResult = {
|
|
42
|
+
ok: true;
|
|
43
|
+
} | {
|
|
44
|
+
ok: false;
|
|
45
|
+
reason: CapabilityVerifyReason;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Produce the canonical byte representation of a capability body for signing.
|
|
49
|
+
*
|
|
50
|
+
* (RFC 8032 signing input) — copy every field EXCEPT `sig`, sort object keys lexicographically at every
|
|
51
|
+
* nesting level, serialize as whitespace-free JSON, UTF-8 encode. Identical scheme to the manifest.
|
|
52
|
+
*/
|
|
53
|
+
export declare function canonicalCapabilityBody(cap: PreAuthCapabilityBody): Uint8Array;
|
|
54
|
+
/**
|
|
55
|
+
* Serialize a capability for transport and CLI: base64url of its JSON. Carried in the existing
|
|
56
|
+
* round-1 `preAuthToken` string field (no wire-schema change) and pasted into `cello register`.
|
|
57
|
+
*/
|
|
58
|
+
export declare function encodeCapability(cap: PreAuthCapability): string;
|
|
59
|
+
/** Parse a serialized capability blob; null if it is not valid base64url JSON (verify validates shape). */
|
|
60
|
+
export declare function decodeCapability(blob: string): PreAuthCapability | null;
|
|
61
|
+
/** Sign a capability body with the issuer's Ed25519 key, returning the full capability with `sig` (hex). */
|
|
62
|
+
export declare function signCapability(body: PreAuthCapabilityBody, issuer: KeyProvider): Promise<PreAuthCapability>;
|
|
63
|
+
/**
|
|
64
|
+
* Verify a capability against the pinned issuer public key and validity window. NEVER throws — every
|
|
65
|
+
* failure is a result value.
|
|
66
|
+
*
|
|
67
|
+
* (RFC 8032 verification)
|
|
68
|
+
* 1. Structural check: all body fields present and string, `sig` is 64-byte hex → else `capability_malformed`.
|
|
69
|
+
* 2. Ed25519.verify(sig, canonicalCapabilityBody, issuerPubkey) → else `capability_signature_invalid`.
|
|
70
|
+
* 3. now >= expires_at → `capability_expired`.
|
|
71
|
+
*/
|
|
72
|
+
export declare function verifyCapability(cap: PreAuthCapability, issuerPubkeyHex: string, now?: Date): CapabilityVerifyResult;
|
|
73
|
+
//# sourceMappingURL=preauth-capability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preauth-capability.d.ts","sourceRoot":"","sources":["../src/preauth-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,oFAAoF;AACpF,MAAM,WAAW,qBAAqB;IACpC,0GAA0G;IAC1G,KAAK,EAAE,MAAM,CAAC;IACd,mHAAmH;IACnH,eAAe,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,2GAA2G;AAC3G,MAAM,WAAW,iBAAkB,SAAQ,qBAAqB;IAC9D,oGAAoG;IACpG,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,sBAAsB,GAC9B,sBAAsB,GACtB,8BAA8B,GAC9B,oBAAoB,CAAC;AAEzB,MAAM,MAAM,sBAAsB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,CAAC;AAUlG;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,qBAAqB,GAAG,UAAU,CAM9E;AAYD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAE/D;AAED,2GAA2G;AAC3G,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAkBvE;AAED,4GAA4G;AAC5G,wBAAsB,cAAc,CAClC,IAAI,EAAE,qBAAqB,EAC3B,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAG5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,iBAAiB,EACtB,eAAe,EAAE,MAAM,EACvB,GAAG,GAAE,IAAiB,GACrB,sBAAsB,CA2BxB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { verify } from "./ed25519.js";
|
|
2
|
+
const BODY_FIELDS = [
|
|
3
|
+
"nonce",
|
|
4
|
+
"phone_stub_hash",
|
|
5
|
+
"email_domain",
|
|
6
|
+
"issued_at",
|
|
7
|
+
"expires_at",
|
|
8
|
+
];
|
|
9
|
+
/**
|
|
10
|
+
* Produce the canonical byte representation of a capability body for signing.
|
|
11
|
+
*
|
|
12
|
+
* (RFC 8032 signing input) — copy every field EXCEPT `sig`, sort object keys lexicographically at every
|
|
13
|
+
* nesting level, serialize as whitespace-free JSON, UTF-8 encode. Identical scheme to the manifest.
|
|
14
|
+
*/
|
|
15
|
+
export function canonicalCapabilityBody(cap) {
|
|
16
|
+
const body = {};
|
|
17
|
+
for (const key of Object.keys(cap)) {
|
|
18
|
+
if (key !== "sig")
|
|
19
|
+
body[key] = cap[key];
|
|
20
|
+
}
|
|
21
|
+
return new TextEncoder().encode(JSON.stringify(body, sortedReplacer));
|
|
22
|
+
}
|
|
23
|
+
/** JSON.stringify replacer that sorts object keys lexicographically at every level (arrays keep order). */
|
|
24
|
+
function sortedReplacer(_key, value) {
|
|
25
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
26
|
+
const sorted = {};
|
|
27
|
+
for (const k of Object.keys(value).sort())
|
|
28
|
+
sorted[k] = value[k];
|
|
29
|
+
return sorted;
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Serialize a capability for transport and CLI: base64url of its JSON. Carried in the existing
|
|
35
|
+
* round-1 `preAuthToken` string field (no wire-schema change) and pasted into `cello register`.
|
|
36
|
+
*/
|
|
37
|
+
export function encodeCapability(cap) {
|
|
38
|
+
return Buffer.from(JSON.stringify(cap), "utf8").toString("base64url");
|
|
39
|
+
}
|
|
40
|
+
/** Parse a serialized capability blob; null if it is not valid base64url JSON (verify validates shape). */
|
|
41
|
+
export function decodeCapability(blob) {
|
|
42
|
+
try {
|
|
43
|
+
// Fix #2a: robust to copy-paste mangling. A long token gets word-wrapped, and copying it can inject
|
|
44
|
+
// spaces/newlines/CRs that would otherwise corrupt the decode (the token "silently" fails or
|
|
45
|
+
// truncates). Strip ALL whitespace first, and normalize a standard-base64 paste (+ / and = padding)
|
|
46
|
+
// to base64url so either alphabet round-trips.
|
|
47
|
+
const cleaned = blob
|
|
48
|
+
.replace(/\s+/g, "")
|
|
49
|
+
.replace(/\+/g, "-")
|
|
50
|
+
.replace(/\//g, "_")
|
|
51
|
+
.replace(/=+$/, "");
|
|
52
|
+
if (cleaned.length === 0)
|
|
53
|
+
return null;
|
|
54
|
+
const obj = JSON.parse(Buffer.from(cleaned, "base64url").toString("utf8"));
|
|
55
|
+
if (obj === null || typeof obj !== "object")
|
|
56
|
+
return null;
|
|
57
|
+
return obj;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** Sign a capability body with the issuer's Ed25519 key, returning the full capability with `sig` (hex). */
|
|
64
|
+
export async function signCapability(body, issuer) {
|
|
65
|
+
const sig = await issuer.sign(canonicalCapabilityBody(body));
|
|
66
|
+
return { ...body, sig: Buffer.from(sig).toString("hex") };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Verify a capability against the pinned issuer public key and validity window. NEVER throws — every
|
|
70
|
+
* failure is a result value.
|
|
71
|
+
*
|
|
72
|
+
* (RFC 8032 verification)
|
|
73
|
+
* 1. Structural check: all body fields present and string, `sig` is 64-byte hex → else `capability_malformed`.
|
|
74
|
+
* 2. Ed25519.verify(sig, canonicalCapabilityBody, issuerPubkey) → else `capability_signature_invalid`.
|
|
75
|
+
* 3. now >= expires_at → `capability_expired`.
|
|
76
|
+
*/
|
|
77
|
+
export function verifyCapability(cap, issuerPubkeyHex, now = new Date()) {
|
|
78
|
+
// 1. Structural validation — a malformed capability must fail loudly, never verify by accident.
|
|
79
|
+
if (cap === null || typeof cap !== "object")
|
|
80
|
+
return { ok: false, reason: "capability_malformed" };
|
|
81
|
+
for (const f of BODY_FIELDS) {
|
|
82
|
+
if (typeof cap[f] !== "string") {
|
|
83
|
+
return { ok: false, reason: "capability_malformed" };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const sigBytes = hexToBytes(cap.sig, 64);
|
|
87
|
+
const pubBytes = hexToBytes(issuerPubkeyHex, 32);
|
|
88
|
+
if (sigBytes === null || pubBytes === null)
|
|
89
|
+
return { ok: false, reason: "capability_malformed" };
|
|
90
|
+
// 2. Signature over the canonical body.
|
|
91
|
+
let valid = false;
|
|
92
|
+
try {
|
|
93
|
+
valid = verify(pubBytes, canonicalCapabilityBody(cap), sigBytes);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
valid = false;
|
|
97
|
+
}
|
|
98
|
+
if (!valid)
|
|
99
|
+
return { ok: false, reason: "capability_signature_invalid" };
|
|
100
|
+
// 3. Validity window — invalid at or after expiry.
|
|
101
|
+
const expires = new Date(cap.expires_at);
|
|
102
|
+
if (Number.isNaN(expires.getTime()) || now >= expires) {
|
|
103
|
+
return { ok: false, reason: "capability_expired" };
|
|
104
|
+
}
|
|
105
|
+
return { ok: true };
|
|
106
|
+
}
|
|
107
|
+
/** Decode a hex string to exactly `expectedBytes` bytes, or null if malformed. */
|
|
108
|
+
function hexToBytes(hex, expectedBytes) {
|
|
109
|
+
if (typeof hex !== "string" || hex.length !== expectedBytes * 2 || !/^[0-9a-fA-F]+$/.test(hex)) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const out = new Uint8Array(expectedBytes);
|
|
113
|
+
for (let i = 0; i < expectedBytes; i++)
|
|
114
|
+
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=preauth-capability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preauth-capability.js","sourceRoot":"","sources":["../src/preauth-capability.ts"],"names":[],"mappings":"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"}
|