@bitspark/archon-sdk 0.8.2 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/enroll.d.ts +49 -0
- package/dist/src/enroll.js +140 -0
- package/dist/src/enroll.js.map +1 -0
- package/dist/src/envelope.d.ts +15 -0
- package/dist/src/envelope.js +36 -1
- package/dist/src/envelope.js.map +1 -1
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +7 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/login.d.ts +17 -0
- package/dist/src/login.js +28 -1
- package/dist/src/login.js.map +1 -1
- package/dist/src/possession.d.ts +11 -0
- package/dist/src/possession.js +25 -0
- package/dist/src/possession.js.map +1 -1
- package/dist/src/request.d.ts +95 -0
- package/dist/src/request.js +305 -0
- package/dist/src/request.js.map +1 -0
- package/dist/src/signer.d.ts +54 -0
- package/dist/src/signer.js +116 -0
- package/dist/src/signer.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type Signer, type SigningRequest } from "./signer.js";
|
|
2
|
+
/** The RFC 8032 context every enrollment proof is made in. */
|
|
3
|
+
export declare const ENROLL_DOMAIN = "archon-enroll/1";
|
|
4
|
+
/** The first byte of every enrollment binding. */
|
|
5
|
+
export declare const ENROLL_VERSION = 1;
|
|
6
|
+
/** The longest purpose, in bytes. */
|
|
7
|
+
export declare const ENROLL_MAX_PURPOSE_SIZE = 255;
|
|
8
|
+
/** The longest transaction id, in bytes. */
|
|
9
|
+
export declare const ENROLL_MAX_TRANSACTION_SIZE = 255;
|
|
10
|
+
/** The intent digest's length: a SHA-256. */
|
|
11
|
+
export declare const ENROLL_DIGEST_SIZE = 32;
|
|
12
|
+
/** A pending enrollment as the service recorded it. */
|
|
13
|
+
export interface EnrollRequest {
|
|
14
|
+
/** The server's fresh entropy, at least the possession scheme's 16 bytes. */
|
|
15
|
+
nonce: Uint8Array;
|
|
16
|
+
/** The pending transaction's id, 1..=255 bytes, opaque. */
|
|
17
|
+
transaction: Uint8Array;
|
|
18
|
+
/** "add-key", "rotate", "recover", …: 1..=255 bytes of UTF-8, no control characters. */
|
|
19
|
+
purpose: string;
|
|
20
|
+
/** The public key being enrolled, exactly PUBLIC_KEY_SIZE bytes. */
|
|
21
|
+
newKey: Uint8Array;
|
|
22
|
+
/** SHA-256 of the service's immutable intent bytes, exactly ENROLL_DIGEST_SIZE bytes. */
|
|
23
|
+
intentDigest: Uint8Array;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The bytes an enrollment proof is bound to, for `req` at `audience`. Throws on a purpose that
|
|
27
|
+
* is empty, over 255 bytes, not UTF-8 or carrying a control character (U+0000–U+001F, U+007F);
|
|
28
|
+
* an audience that is empty, not UTF-8 or carrying a control character; a transaction id that
|
|
29
|
+
* is empty or over 255 bytes; a new key or intent digest of the wrong size; or a binding that
|
|
30
|
+
* would not fit the possession scheme's u16 field.
|
|
31
|
+
*/
|
|
32
|
+
export declare function enrollBinding(audience: string, req: EnrollRequest): Uint8Array;
|
|
33
|
+
/**
|
|
34
|
+
* The enrollment proof: possession by the key behind `seed` — which must be `req.newKey` — in
|
|
35
|
+
* ENROLL_DOMAIN, over `req.nonce` and `enrollBinding(audience, req)`. Throws for what the binding
|
|
36
|
+
* throws, for a short nonce, for a seed of the wrong size, and for a seed that is not the new
|
|
37
|
+
* key: only the key being enrolled can prove it holds itself.
|
|
38
|
+
*/
|
|
39
|
+
export declare function proveEnroll(seed: Uint8Array, audience: string, req: EnrollRequest): Uint8Array;
|
|
40
|
+
/** Whether `signature` is the enrollment proof by `req.newKey` for `req` at `audience`. Total. */
|
|
41
|
+
export declare function verifyEnroll(audience: string, req: EnrollRequest, signature: Uint8Array): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* The signing request the enrollment proof needs (ADR 0009 §4). Pure. The expected key is
|
|
44
|
+
* `req.newKey`, so only that key's signer can complete it — `proveEnroll`'s refusal of any other
|
|
45
|
+
* seed, carried into the request itself. Complete it with `completePossession`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function prepareEnroll(audience: string, req: EnrollRequest): SigningRequest;
|
|
48
|
+
/** `proveEnroll` through a signer instead of a seed. A signer for any other key is refused. */
|
|
49
|
+
export declare function proveEnrollWith(signer: Signer, audience: string, req: EnrollRequest, signal?: AbortSignal): Promise<Uint8Array>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Key enrollment — THIS NEW KEY BELONGS TO THIS ACCOUNT (ADR 0010 §7; docs/request.md §6,
|
|
2
|
+
// version 1, fixed by ADR 0010's status note of 4 October 2026).
|
|
3
|
+
//
|
|
4
|
+
// Login has a person approve a browser key. Enrollment is a different statement: a NEW key
|
|
5
|
+
// proves its own possession, while a separate authority — a session, or a bootstrap credential
|
|
6
|
+
// — says whose key it becomes. That authority lives in the service's pending transaction
|
|
7
|
+
// record, never in this proof: the service creates the record only after validating the
|
|
8
|
+
// authority, and the verifier rebuilds the binding from that record and its configured
|
|
9
|
+
// audience. A completion request names the transaction and carries the proof, nothing more.
|
|
10
|
+
//
|
|
11
|
+
// The proof is the possession scheme with the server's fresh nonce, in ENROLL_DOMAIN, over
|
|
12
|
+
//
|
|
13
|
+
// version ‖ u16be(len purpose) ‖ purpose ‖ u16be(len audience) ‖ audience
|
|
14
|
+
// ‖ u16be(len transaction) ‖ transaction ‖ new_key[32] ‖ intent_digest[32]
|
|
15
|
+
//
|
|
16
|
+
// The intent digest is the service's SHA-256 of its immutable enrollment intent; archon binds
|
|
17
|
+
// the 32 bytes and never reads what they digest. Enrollment shows that an account and a key are
|
|
18
|
+
// associated — not that the key is non-exportable, lives on one device, or is used by one
|
|
19
|
+
// process.
|
|
20
|
+
import { getPublicKey, PUBLIC_KEY_SIZE, SEED_SIZE } from "@bitspark/archon";
|
|
21
|
+
import { completePossession, MAX_FIELD_SIZE as POSSESSION_MAX_FIELD_SIZE, preparePossession, provePossession, verifyPossession, } from "./possession.js";
|
|
22
|
+
import { signWith } from "./signer.js";
|
|
23
|
+
/** The RFC 8032 context every enrollment proof is made in. */
|
|
24
|
+
export const ENROLL_DOMAIN = "archon-enroll/1";
|
|
25
|
+
/** The first byte of every enrollment binding. */
|
|
26
|
+
export const ENROLL_VERSION = 0x01;
|
|
27
|
+
/** The longest purpose, in bytes. */
|
|
28
|
+
export const ENROLL_MAX_PURPOSE_SIZE = 255;
|
|
29
|
+
/** The longest transaction id, in bytes. */
|
|
30
|
+
export const ENROLL_MAX_TRANSACTION_SIZE = 255;
|
|
31
|
+
/** The intent digest's length: a SHA-256. */
|
|
32
|
+
export const ENROLL_DIGEST_SIZE = 32;
|
|
33
|
+
const utf8 = new TextEncoder();
|
|
34
|
+
/**
|
|
35
|
+
* The bytes an enrollment proof is bound to, for `req` at `audience`. Throws on a purpose that
|
|
36
|
+
* is empty, over 255 bytes, not UTF-8 or carrying a control character (U+0000–U+001F, U+007F);
|
|
37
|
+
* an audience that is empty, not UTF-8 or carrying a control character; a transaction id that
|
|
38
|
+
* is empty or over 255 bytes; a new key or intent digest of the wrong size; or a binding that
|
|
39
|
+
* would not fit the possession scheme's u16 field.
|
|
40
|
+
*/
|
|
41
|
+
export function enrollBinding(audience, req) {
|
|
42
|
+
const purpose = checkText("purpose", req.purpose);
|
|
43
|
+
if (purpose.length > ENROLL_MAX_PURPOSE_SIZE) {
|
|
44
|
+
throw new Error(`enroll: purpose is ${purpose.length} bytes, want 1..=${ENROLL_MAX_PURPOSE_SIZE}`);
|
|
45
|
+
}
|
|
46
|
+
const aud = checkText("audience", audience);
|
|
47
|
+
if (req.transaction.length === 0 || req.transaction.length > ENROLL_MAX_TRANSACTION_SIZE) {
|
|
48
|
+
throw new Error(`enroll: transaction is ${req.transaction.length} bytes, want 1..=${ENROLL_MAX_TRANSACTION_SIZE}`);
|
|
49
|
+
}
|
|
50
|
+
if (req.newKey.length !== PUBLIC_KEY_SIZE) {
|
|
51
|
+
throw new Error(`enroll: new key is ${req.newKey.length} bytes, want ${PUBLIC_KEY_SIZE}`);
|
|
52
|
+
}
|
|
53
|
+
if (req.intentDigest.length !== ENROLL_DIGEST_SIZE) {
|
|
54
|
+
throw new Error(`enroll: intent digest is ${req.intentDigest.length} bytes, want ${ENROLL_DIGEST_SIZE}`);
|
|
55
|
+
}
|
|
56
|
+
const size = 1 + 2 + purpose.length + 2 + aud.length + 2 + req.transaction.length + PUBLIC_KEY_SIZE + ENROLL_DIGEST_SIZE;
|
|
57
|
+
if (size > POSSESSION_MAX_FIELD_SIZE) {
|
|
58
|
+
throw new Error(`enroll: binding is ${size} bytes, over the possession scheme's ${POSSESSION_MAX_FIELD_SIZE}`);
|
|
59
|
+
}
|
|
60
|
+
const out = new Uint8Array(size);
|
|
61
|
+
let at = 0;
|
|
62
|
+
out[at++] = ENROLL_VERSION;
|
|
63
|
+
at = putField(out, at, purpose);
|
|
64
|
+
at = putField(out, at, aud);
|
|
65
|
+
at = putField(out, at, req.transaction);
|
|
66
|
+
out.set(req.newKey, at);
|
|
67
|
+
at += PUBLIC_KEY_SIZE;
|
|
68
|
+
out.set(req.intentDigest, at);
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The enrollment proof: possession by the key behind `seed` — which must be `req.newKey` — in
|
|
73
|
+
* ENROLL_DOMAIN, over `req.nonce` and `enrollBinding(audience, req)`. Throws for what the binding
|
|
74
|
+
* throws, for a short nonce, for a seed of the wrong size, and for a seed that is not the new
|
|
75
|
+
* key: only the key being enrolled can prove it holds itself.
|
|
76
|
+
*/
|
|
77
|
+
export function proveEnroll(seed, audience, req) {
|
|
78
|
+
if (seed.length !== SEED_SIZE)
|
|
79
|
+
throw new Error(`enroll: seed is ${seed.length} bytes, want ${SEED_SIZE}`);
|
|
80
|
+
const pub = getPublicKey(seed);
|
|
81
|
+
if (pub.length !== req.newKey.length || !pub.every((b, i) => b === req.newKey[i])) {
|
|
82
|
+
throw new Error("enroll: seed is not the new key the request names");
|
|
83
|
+
}
|
|
84
|
+
return provePossession(seed, ENROLL_DOMAIN, req.nonce, enrollBinding(audience, req));
|
|
85
|
+
}
|
|
86
|
+
/** Whether `signature` is the enrollment proof by `req.newKey` for `req` at `audience`. Total. */
|
|
87
|
+
export function verifyEnroll(audience, req, signature) {
|
|
88
|
+
let binding;
|
|
89
|
+
try {
|
|
90
|
+
binding = enrollBinding(audience, req);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
return verifyPossession(req.newKey, ENROLL_DOMAIN, req.nonce, binding, signature);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The signing request the enrollment proof needs (ADR 0009 §4). Pure. The expected key is
|
|
99
|
+
* `req.newKey`, so only that key's signer can complete it — `proveEnroll`'s refusal of any other
|
|
100
|
+
* seed, carried into the request itself. Complete it with `completePossession`.
|
|
101
|
+
*/
|
|
102
|
+
export function prepareEnroll(audience, req) {
|
|
103
|
+
return preparePossession(req.newKey, ENROLL_DOMAIN, req.nonce, enrollBinding(audience, req));
|
|
104
|
+
}
|
|
105
|
+
/** `proveEnroll` through a signer instead of a seed. A signer for any other key is refused. */
|
|
106
|
+
export async function proveEnrollWith(signer, audience, req, signal) {
|
|
107
|
+
const request = prepareEnroll(audience, req);
|
|
108
|
+
return completePossession(request, await signWith(signer, request, signal));
|
|
109
|
+
}
|
|
110
|
+
/** Throws unless `s` is non-empty, well-formed UTF-8 (no lone surrogate) with no control
|
|
111
|
+
* character; returns its UTF-8 bytes. */
|
|
112
|
+
function checkText(what, s) {
|
|
113
|
+
if (!isWellFormed(s))
|
|
114
|
+
throw new Error(`enroll: ${what} is not well-formed UTF-16 (a lone surrogate is not UTF-8)`);
|
|
115
|
+
for (const ch of s) {
|
|
116
|
+
const cp = ch.codePointAt(0);
|
|
117
|
+
if (cp < 0x20 || cp === 0x7f) {
|
|
118
|
+
throw new Error(`enroll: ${what} carries a control character U+${cp.toString(16).toUpperCase().padStart(4, "0")}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const bytes = utf8.encode(s);
|
|
122
|
+
if (bytes.length === 0 || bytes.length > POSSESSION_MAX_FIELD_SIZE) {
|
|
123
|
+
throw new Error(`enroll: ${what} is ${bytes.length} bytes, want 1..=${POSSESSION_MAX_FIELD_SIZE}`);
|
|
124
|
+
}
|
|
125
|
+
return bytes;
|
|
126
|
+
}
|
|
127
|
+
function isWellFormed(s) {
|
|
128
|
+
const native = s.isWellFormed;
|
|
129
|
+
if (typeof native === "function")
|
|
130
|
+
return native.call(s);
|
|
131
|
+
return !/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/.test(s);
|
|
132
|
+
}
|
|
133
|
+
/** Writes u16be(len field) ‖ field at `at`; returns the new offset. */
|
|
134
|
+
function putField(out, at, field) {
|
|
135
|
+
out[at++] = (field.length >> 8) & 0xff;
|
|
136
|
+
out[at++] = field.length & 0xff;
|
|
137
|
+
out.set(field, at);
|
|
138
|
+
return at + field.length;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=enroll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enroll.js","sourceRoot":"","sources":["../../src/enroll.ts"],"names":[],"mappings":"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"}
|
package/dist/src/envelope.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Signer, type SigningRequest } from "./signer.js";
|
|
1
2
|
/** The first four bytes of every envelope. */
|
|
2
3
|
export declare const ENVELOPE_MAGIC: Uint8Array<ArrayBuffer>;
|
|
3
4
|
/** The envelope format version. */
|
|
@@ -17,6 +18,20 @@ export interface Opened {
|
|
|
17
18
|
* because of the scheme tag.
|
|
18
19
|
*/
|
|
19
20
|
export declare function seal(seed: Uint8Array, domain: string, payload: Uint8Array): Uint8Array;
|
|
21
|
+
/**
|
|
22
|
+
* The signing request sealing `payload` in `domain` needs, for a signer whose key is
|
|
23
|
+
* `publicKey` (ADR 0009 §4). Pure; throws on an invalid domain. The message is
|
|
24
|
+
* `envelopeMessageBytes(payload)`, so an envelope made through it is the same bytes.
|
|
25
|
+
*/
|
|
26
|
+
export declare function prepareSeal(publicKey: Uint8Array, domain: string, payload: Uint8Array): SigningRequest;
|
|
27
|
+
/**
|
|
28
|
+
* The envelope, from a signature over a prepared seal request: the signature is checked
|
|
29
|
+
* against the request, then the envelope is assembled from the request's own key, domain and
|
|
30
|
+
* payload. Pure.
|
|
31
|
+
*/
|
|
32
|
+
export declare function completeSeal(request: SigningRequest, signature: Uint8Array): Uint8Array;
|
|
33
|
+
/** `seal` through a signer instead of a seed. */
|
|
34
|
+
export declare function sealWith(signer: Signer, domain: string, payload: Uint8Array, signal?: AbortSignal): Promise<Uint8Array>;
|
|
20
35
|
/**
|
|
21
36
|
* Open `envelope`, which the caller expects to be sealed in `domain`. Throws — never
|
|
22
37
|
* returns a payload — when the bytes are not an envelope (magic, version, length), the
|
package/dist/src/envelope.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// another. The verifier chooses the domain; an envelope never gets to choose it for
|
|
16
16
|
// them. Whether to trust the key it names is, again, the verifier's.
|
|
17
17
|
import { getPublicKey, signInDomain, verifyInDomain, MAX_DOMAIN_SIZE, PUBLIC_KEY_SIZE, SIGNATURE_SIZE, } from "@bitspark/archon";
|
|
18
|
+
import { checkSignature, signWith, validateSigningRequest } from "./signer.js";
|
|
18
19
|
/** The first four bytes of every envelope. */
|
|
19
20
|
export const ENVELOPE_MAGIC = new Uint8Array([0x61, 0x72, 0x63, 0x6e]); // "arcn"
|
|
20
21
|
/** The envelope format version. */
|
|
@@ -28,7 +29,41 @@ export const ENVELOPE_SCHEME_TAG = 0x02;
|
|
|
28
29
|
*/
|
|
29
30
|
export function seal(seed, domain, payload) {
|
|
30
31
|
const signature = signInDomain(seed, domain, envelopeMessageBytes(payload));
|
|
31
|
-
|
|
32
|
+
return pack(domain, getPublicKey(seed), signature, payload);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The signing request sealing `payload` in `domain` needs, for a signer whose key is
|
|
36
|
+
* `publicKey` (ADR 0009 §4). Pure; throws on an invalid domain. The message is
|
|
37
|
+
* `envelopeMessageBytes(payload)`, so an envelope made through it is the same bytes.
|
|
38
|
+
*/
|
|
39
|
+
export function prepareSeal(publicKey, domain, payload) {
|
|
40
|
+
const request = {
|
|
41
|
+
expectedPublicKey: publicKey,
|
|
42
|
+
scheme: { kind: "ed25519ph-context", domain },
|
|
43
|
+
message: envelopeMessageBytes(payload),
|
|
44
|
+
};
|
|
45
|
+
validateSigningRequest(request);
|
|
46
|
+
return request;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The envelope, from a signature over a prepared seal request: the signature is checked
|
|
50
|
+
* against the request, then the envelope is assembled from the request's own key, domain and
|
|
51
|
+
* payload. Pure.
|
|
52
|
+
*/
|
|
53
|
+
export function completeSeal(request, signature) {
|
|
54
|
+
if (request.scheme.kind !== "ed25519ph-context" || request.message[0] !== ENVELOPE_SCHEME_TAG) {
|
|
55
|
+
throw new Error("not a seal request");
|
|
56
|
+
}
|
|
57
|
+
checkSignature(request, signature);
|
|
58
|
+
return pack(request.scheme.domain, request.expectedPublicKey, signature, request.message.subarray(1));
|
|
59
|
+
}
|
|
60
|
+
/** `seal` through a signer instead of a seed. */
|
|
61
|
+
export async function sealWith(signer, domain, payload, signal) {
|
|
62
|
+
const request = prepareSeal(signer.publicKey, domain, payload);
|
|
63
|
+
return completeSeal(request, await signWith(signer, request, signal));
|
|
64
|
+
}
|
|
65
|
+
/** The container layout, from parts already checked: the domain fits a u8 (≤ 255 bytes). */
|
|
66
|
+
function pack(domain, pubkey, signature, payload) {
|
|
32
67
|
const d = new TextEncoder().encode(domain);
|
|
33
68
|
const out = new Uint8Array(4 + 1 + 1 + d.length + PUBLIC_KEY_SIZE + SIGNATURE_SIZE + payload.length);
|
|
34
69
|
let at = 0;
|
package/dist/src/envelope.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/envelope.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,wEAAwE;AACxE,EAAE;AACF,mFAAmF;AACnF,EAAE;AACF,sFAAsF;AACtF,mFAAmF;AACnF,mFAAmF;AACnF,wFAAwF;AACxF,wFAAwF;AACxF,0BAA0B;AAC1B,EAAE;AACF,mFAAmF;AACnF,oFAAoF;AACpF,qEAAqE;AACrE,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,8CAA8C;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;AACjF,mCAAmC;AACnC,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AACrC,kGAAkG;AAClG,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAUxC;;;;GAIG;AACH,MAAM,UAAU,IAAI,CAAC,IAAgB,EAAE,MAAc,EAAE,OAAmB;IACxE,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5E,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/envelope.ts"],"names":[],"mappings":"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"}
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -7,13 +7,20 @@
|
|
|
7
7
|
// binding is what makes it a proof, so an empty one is refused.
|
|
8
8
|
// - envelope — these bytes, signed by this key, in this domain. JWS, never JWT.
|
|
9
9
|
//
|
|
10
|
+
// Each signing helper takes a seed, or — through its prepare / complete pair and its `…With`
|
|
11
|
+
// form — a Signer (ADR 0009 §4): signing capability rather than the key, for keys that do not
|
|
12
|
+
// live in this process.
|
|
13
|
+
//
|
|
10
14
|
// The rule that keeps this layer honest: entropy, time and channel binding are
|
|
11
15
|
// ARGUMENTS. This package never sources them. That is what lets every byte it emits be a
|
|
12
16
|
// deterministic function of its inputs and be pinned by vectors/sdk.json across three
|
|
13
17
|
// languages — the same way the floor is pinned. Connection setup is not here and never
|
|
14
18
|
// will be: that is the transport's (ADR 0004).
|
|
19
|
+
export * from "./signer.js";
|
|
15
20
|
export * from "./possession.js";
|
|
16
21
|
export * from "./envelope.js";
|
|
17
22
|
export * from "./login.js";
|
|
18
23
|
export * from "./login-audience.js";
|
|
24
|
+
export * from "./enroll.js";
|
|
25
|
+
export * from "./request.js";
|
|
19
26
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,uFAAuF;AACvF,wEAAwE;AACxE,EAAE;AACF,qFAAqF;AACrF,kEAAkE;AAClE,gFAAgF;AAChF,EAAE;AACF,+EAA+E;AAC/E,yFAAyF;AACzF,sFAAsF;AACtF,uFAAuF;AACvF,+CAA+C;AAC/C,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,uFAAuF;AACvF,wEAAwE;AACxE,EAAE;AACF,qFAAqF;AACrF,kEAAkE;AAClE,gFAAgF;AAChF,EAAE;AACF,6FAA6F;AAC7F,8FAA8F;AAC9F,wBAAwB;AACxB,EAAE;AACF,+EAA+E;AAC/E,yFAAyF;AACzF,sFAAsF;AACtF,uFAAuF;AACvF,+CAA+C;AAC/C,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
package/dist/src/login.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Signer, type SigningRequest } from "./signer.js";
|
|
1
2
|
/** The RFC 8032 context every login-scheme proof is made in. */
|
|
2
3
|
export declare const LOGIN_DOMAIN = "archon-login/1";
|
|
3
4
|
/** The binding's first byte for the person's proof (made by P). */
|
|
@@ -59,5 +60,21 @@ export declare function verifyLogin(pub: Uint8Array, audience: string, req: Logi
|
|
|
59
60
|
* `req.browser` throws: the proof is only meaningful from the key the request names.
|
|
60
61
|
*/
|
|
61
62
|
export declare function proveCollect(seed: Uint8Array, audience: string, req: LoginRequest): Uint8Array;
|
|
63
|
+
/**
|
|
64
|
+
* The signing request the person's login proof needs, for a signer whose key is `publicKey`
|
|
65
|
+
* (ADR 0009 §4). Pure; throws for what `proveLogin` throws about the request. Complete it
|
|
66
|
+
* with `completePossession`: a login proof is a possession proof.
|
|
67
|
+
*/
|
|
68
|
+
export declare function prepareLogin(publicKey: Uint8Array, audience: string, req: LoginRequest): SigningRequest;
|
|
69
|
+
/** `proveLogin` through a signer instead of a seed. */
|
|
70
|
+
export declare function proveLoginWith(signer: Signer, audience: string, req: LoginRequest, signal?: AbortSignal): Promise<Uint8Array>;
|
|
71
|
+
/**
|
|
72
|
+
* The signing request the browser's collect proof needs. Pure. The expected key is the
|
|
73
|
+
* browser key `req` names, so only that key's signer can complete it — `proveCollect`'s
|
|
74
|
+
* refusal of any other seed, carried into the request itself.
|
|
75
|
+
*/
|
|
76
|
+
export declare function prepareCollect(audience: string, req: LoginRequest): SigningRequest;
|
|
77
|
+
/** `proveCollect` through a signer instead of a seed. A signer for any other key is refused. */
|
|
78
|
+
export declare function proveCollectWith(signer: Signer, audience: string, req: LoginRequest, signal?: AbortSignal): Promise<Uint8Array>;
|
|
62
79
|
/** Whether `signature` is the collect proof by `req.browser` for `req` at `audience`. Total. */
|
|
63
80
|
export declare function verifyCollect(audience: string, req: LoginRequest, signature: Uint8Array): boolean;
|
package/dist/src/login.js
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
// the collect proof (0x02) is made by K when the browser collects the answer, so a bystander
|
|
18
18
|
// who saw the request id cannot consume the login.
|
|
19
19
|
import { getPublicKey, PUBLIC_KEY_SIZE, SEED_SIZE } from "@bitspark/archon";
|
|
20
|
-
import { MAX_FIELD_SIZE as POSSESSION_MAX_FIELD_SIZE, provePossession, verifyPossession } from "./possession.js";
|
|
20
|
+
import { completePossession, MAX_FIELD_SIZE as POSSESSION_MAX_FIELD_SIZE, preparePossession, provePossession, verifyPossession, } from "./possession.js";
|
|
21
|
+
import { signWith } from "./signer.js";
|
|
21
22
|
/** The RFC 8032 context every login-scheme proof is made in. */
|
|
22
23
|
export const LOGIN_DOMAIN = "archon-login/1";
|
|
23
24
|
/** The binding's first byte for the person's proof (made by P). */
|
|
@@ -123,6 +124,32 @@ export function proveCollect(seed, audience, req) {
|
|
|
123
124
|
}
|
|
124
125
|
return provePossession(seed, LOGIN_DOMAIN, req.nonce, loginBinding(LOGIN_ROLE_COLLECT, audience, req));
|
|
125
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* The signing request the person's login proof needs, for a signer whose key is `publicKey`
|
|
129
|
+
* (ADR 0009 §4). Pure; throws for what `proveLogin` throws about the request. Complete it
|
|
130
|
+
* with `completePossession`: a login proof is a possession proof.
|
|
131
|
+
*/
|
|
132
|
+
export function prepareLogin(publicKey, audience, req) {
|
|
133
|
+
return preparePossession(publicKey, LOGIN_DOMAIN, req.nonce, loginBinding(LOGIN_ROLE_LOGIN, audience, req));
|
|
134
|
+
}
|
|
135
|
+
/** `proveLogin` through a signer instead of a seed. */
|
|
136
|
+
export async function proveLoginWith(signer, audience, req, signal) {
|
|
137
|
+
const request = prepareLogin(signer.publicKey, audience, req);
|
|
138
|
+
return completePossession(request, await signWith(signer, request, signal));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The signing request the browser's collect proof needs. Pure. The expected key is the
|
|
142
|
+
* browser key `req` names, so only that key's signer can complete it — `proveCollect`'s
|
|
143
|
+
* refusal of any other seed, carried into the request itself.
|
|
144
|
+
*/
|
|
145
|
+
export function prepareCollect(audience, req) {
|
|
146
|
+
return preparePossession(req.browser, LOGIN_DOMAIN, req.nonce, loginBinding(LOGIN_ROLE_COLLECT, audience, req));
|
|
147
|
+
}
|
|
148
|
+
/** `proveCollect` through a signer instead of a seed. A signer for any other key is refused. */
|
|
149
|
+
export async function proveCollectWith(signer, audience, req, signal) {
|
|
150
|
+
const request = prepareCollect(audience, req);
|
|
151
|
+
return completePossession(request, await signWith(signer, request, signal));
|
|
152
|
+
}
|
|
126
153
|
/** Whether `signature` is the collect proof by `req.browser` for `req` at `audience`. Total. */
|
|
127
154
|
export function verifyCollect(audience, req, signature) {
|
|
128
155
|
let binding;
|
package/dist/src/login.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/login.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,EAAE;AACF,6FAA6F;AAC7F,6FAA6F;AAC7F,yFAAyF;AACzF,yFAAyF;AACzF,4FAA4F;AAC5F,mCAAmC;AACnC,EAAE;AACF,2FAA2F;AAC3F,6FAA6F;AAC7F,4FAA4F;AAC5F,6FAA6F;AAC7F,uDAAuD;AACvD,EAAE;AACF,8FAA8F;AAC9F,6FAA6F;AAC7F,mDAAmD;AACnD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE5E,OAAO,
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/login.ts"],"names":[],"mappings":"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"}
|
package/dist/src/possession.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Signer, type SigningRequest } from "./signer.js";
|
|
1
2
|
/** The first byte of every possession message. Distinct from the envelope's `SCHEME_TAG`. */
|
|
2
3
|
export declare const POSSESSION_SCHEME_TAG = 1;
|
|
3
4
|
/** The shortest nonce accepted, in bytes. Below this a proof is guessable, so it is refused. */
|
|
@@ -16,5 +17,15 @@ export declare function provePossession(seed: Uint8Array, domain: string, nonce:
|
|
|
16
17
|
* nonce, empty binding, wrong-sized key or signature — is `false`.
|
|
17
18
|
*/
|
|
18
19
|
export declare function verifyPossession(pub: Uint8Array, domain: string, nonce: Uint8Array, binding: Uint8Array, signature: Uint8Array): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* The signing request a possession proof needs, for a signer whose key is `publicKey`
|
|
22
|
+
* (ADR 0009 §4). Pure, and throws for what `provePossession` throws. The message is
|
|
23
|
+
* `possessionMessageBytes(nonce, binding)`, so a proof made through it is the same bytes.
|
|
24
|
+
*/
|
|
25
|
+
export declare function preparePossession(publicKey: Uint8Array, domain: string, nonce: Uint8Array, binding: Uint8Array): SigningRequest;
|
|
26
|
+
/** The proof, from a signature over a prepared request: checked, then returned. Pure. */
|
|
27
|
+
export declare function completePossession(request: SigningRequest, signature: Uint8Array): Uint8Array;
|
|
28
|
+
/** `provePossession` through a signer instead of a seed. */
|
|
29
|
+
export declare function provePossessionWith(signer: Signer, domain: string, nonce: Uint8Array, binding: Uint8Array, signal?: AbortSignal): Promise<Uint8Array>;
|
|
19
30
|
/** The pinned layout of what gets signed. Exported so a consumer can pin it too. */
|
|
20
31
|
export declare function possessionMessageBytes(nonce: Uint8Array, binding: Uint8Array): Uint8Array;
|
package/dist/src/possession.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
// possession message and an envelope payload in the same domain from ever being the
|
|
18
18
|
// same bytes.
|
|
19
19
|
import { signInDomain, verifyInDomain } from "@bitspark/archon";
|
|
20
|
+
import { checkSignature, signWith, validateSigningRequest } from "./signer.js";
|
|
20
21
|
/** The first byte of every possession message. Distinct from the envelope's `SCHEME_TAG`. */
|
|
21
22
|
export const POSSESSION_SCHEME_TAG = 0x01;
|
|
22
23
|
/** The shortest nonce accepted, in bytes. Below this a proof is guessable, so it is refused. */
|
|
@@ -46,6 +47,30 @@ export function verifyPossession(pub, domain, nonce, binding, signature) {
|
|
|
46
47
|
}
|
|
47
48
|
return verifyInDomain(pub, domain, message, signature);
|
|
48
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* The signing request a possession proof needs, for a signer whose key is `publicKey`
|
|
52
|
+
* (ADR 0009 §4). Pure, and throws for what `provePossession` throws. The message is
|
|
53
|
+
* `possessionMessageBytes(nonce, binding)`, so a proof made through it is the same bytes.
|
|
54
|
+
*/
|
|
55
|
+
export function preparePossession(publicKey, domain, nonce, binding) {
|
|
56
|
+
const request = {
|
|
57
|
+
expectedPublicKey: publicKey,
|
|
58
|
+
scheme: { kind: "ed25519ph-context", domain },
|
|
59
|
+
message: possessionMessageBytes(nonce, binding),
|
|
60
|
+
};
|
|
61
|
+
validateSigningRequest(request);
|
|
62
|
+
return request;
|
|
63
|
+
}
|
|
64
|
+
/** The proof, from a signature over a prepared request: checked, then returned. Pure. */
|
|
65
|
+
export function completePossession(request, signature) {
|
|
66
|
+
checkSignature(request, signature);
|
|
67
|
+
return new Uint8Array(signature);
|
|
68
|
+
}
|
|
69
|
+
/** `provePossession` through a signer instead of a seed. */
|
|
70
|
+
export async function provePossessionWith(signer, domain, nonce, binding, signal) {
|
|
71
|
+
const request = preparePossession(signer.publicKey, domain, nonce, binding);
|
|
72
|
+
return completePossession(request, await signWith(signer, request, signal));
|
|
73
|
+
}
|
|
49
74
|
/** The pinned layout of what gets signed. Exported so a consumer can pin it too. */
|
|
50
75
|
export function possessionMessageBytes(nonce, binding) {
|
|
51
76
|
if (nonce.length < MIN_NONCE_SIZE)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"possession.js","sourceRoot":"","sources":["../../src/possession.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,kFAAkF;AAClF,oFAAoF;AACpF,uFAAuF;AACvF,0FAA0F;AAC1F,uBAAuB;AACvB,EAAE;AACF,yFAAyF;AACzF,uFAAuF;AACvF,iFAAiF;AACjF,sFAAsF;AACtF,4DAA4D;AAC5D,EAAE;AACF,qFAAqF;AACrF,+EAA+E;AAC/E,oFAAoF;AACpF,cAAc;AACd,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEhE,6FAA6F;AAC7F,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAC1C,gGAAgG;AAChG,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AAErC;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAgB,EAChB,MAAc,EACd,KAAiB,EACjB,OAAmB;IAEnB,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,sBAAsB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAe,EACf,MAAc,EACd,KAAiB,EACjB,OAAmB,EACnB,SAAqB;IAErB,IAAI,OAAmB,CAAC;IACxB,IAAI,CAAC;QACH,OAAO,GAAG,sBAAsB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;AACzD,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,sBAAsB,CAAC,KAAiB,EAAE,OAAmB;IAC3E,IAAI,KAAK,CAAC,MAAM,GAAG,cAAc;QAAE,MAAM,IAAI,KAAK,CAAC,YAAY,KAAK,CAAC,MAAM,eAAe,cAAc,EAAE,CAAC,CAAC;IAC5G,IAAI,KAAK,CAAC,MAAM,GAAG,cAAc;QAAE,MAAM,IAAI,KAAK,CAAC,YAAY,KAAK,CAAC,MAAM,eAAe,cAAc,EAAE,CAAC,CAAC;IAC5G,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IAChG,IAAI,OAAO,CAAC,MAAM,GAAG,cAAc;QAAE,MAAM,IAAI,KAAK,CAAC,cAAc,OAAO,CAAC,MAAM,eAAe,cAAc,EAAE,CAAC,CAAC;IAClH,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtE,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,qBAAqB,CAAC;IAClC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC9B,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IAChC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACnB,EAAE,IAAI,KAAK,CAAC,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;IAChC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAClC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACrB,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
1
|
+
{"version":3,"file":"possession.js","sourceRoot":"","sources":["../../src/possession.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type Signer, type SigningRequest } from "./signer.js";
|
|
2
|
+
/** The RFC 8032 context every request signature is made in. */
|
|
3
|
+
export declare const REQUEST_DOMAIN = "archon-request/1";
|
|
4
|
+
/** The `tag` signature parameter; a verifier accepts only this. */
|
|
5
|
+
export declare const REQUEST_TAG = "archon-request/1";
|
|
6
|
+
/** The one dictionary member in Signature-Input and Signature. */
|
|
7
|
+
export declare const REQUEST_LABEL = "archon";
|
|
8
|
+
/** The nonce's least and greatest size, in bytes. */
|
|
9
|
+
export declare const REQUEST_MIN_NONCE_SIZE = 16;
|
|
10
|
+
export declare const REQUEST_MAX_NONCE_SIZE = 64;
|
|
11
|
+
/** The longest request-target, and the longest covered value, in bytes. */
|
|
12
|
+
export declare const REQUEST_MAX_TARGET_SIZE = 8192;
|
|
13
|
+
export declare const REQUEST_MAX_VALUE_SIZE = 8192;
|
|
14
|
+
/** The largest integer the profile spells: 15 digits. */
|
|
15
|
+
export declare const REQUEST_MAX_INT = 999999999999999;
|
|
16
|
+
/** What a client signs. `declared` holds the product-declared headers the request carries, in
|
|
17
|
+
* the product's declared order, names in lowercase. */
|
|
18
|
+
export interface RequestToSign {
|
|
19
|
+
method: string;
|
|
20
|
+
audience: string;
|
|
21
|
+
requestTarget: string;
|
|
22
|
+
body: Uint8Array;
|
|
23
|
+
contentType?: string | undefined;
|
|
24
|
+
declared: ReadonlyArray<readonly [string, string]>;
|
|
25
|
+
created: number;
|
|
26
|
+
expires: number;
|
|
27
|
+
nonce: Uint8Array;
|
|
28
|
+
}
|
|
29
|
+
/** The four headers archon adds to a request, named in lowercase. */
|
|
30
|
+
export interface RequestHeaders {
|
|
31
|
+
"archon-audience": string;
|
|
32
|
+
"content-digest": string;
|
|
33
|
+
"signature-input": string;
|
|
34
|
+
signature: string;
|
|
35
|
+
}
|
|
36
|
+
/** A request prepared for a signer: the base it will sign, and the signing request. */
|
|
37
|
+
export interface PreparedRequest {
|
|
38
|
+
base: string;
|
|
39
|
+
signing: SigningRequest;
|
|
40
|
+
headers: Omit<RequestHeaders, "signature">;
|
|
41
|
+
}
|
|
42
|
+
/** What a verifier is configured with. */
|
|
43
|
+
export interface RequestPolicy {
|
|
44
|
+
audience: string;
|
|
45
|
+
declared: readonly string[];
|
|
46
|
+
/** W: the longest acceptable `expires − created`, in seconds. */
|
|
47
|
+
maxLifetime: number;
|
|
48
|
+
/** δ: the clock tolerance, in seconds. */
|
|
49
|
+
skew: number;
|
|
50
|
+
}
|
|
51
|
+
/** A request as the server received it: the method, the raw request-target, every header field
|
|
52
|
+
* in the order received (names in any case), and the body after transfer framing. */
|
|
53
|
+
export interface ReceivedRequest {
|
|
54
|
+
method: string;
|
|
55
|
+
requestTarget: string;
|
|
56
|
+
headers: ReadonlyArray<readonly [string, string]>;
|
|
57
|
+
body: Uint8Array;
|
|
58
|
+
}
|
|
59
|
+
/** A proof that checked out under the policy at `now`. Not yet replay-checked. */
|
|
60
|
+
export interface VerifiedRequest {
|
|
61
|
+
principal: Uint8Array;
|
|
62
|
+
keyText: string;
|
|
63
|
+
created: number;
|
|
64
|
+
expires: number;
|
|
65
|
+
nonce: Uint8Array;
|
|
66
|
+
method: string;
|
|
67
|
+
targetUri: string;
|
|
68
|
+
/** Every covered field, in coverage order, with the value that was verified. */
|
|
69
|
+
covered: Array<[string, string]>;
|
|
70
|
+
contentDigest: Uint8Array;
|
|
71
|
+
}
|
|
72
|
+
/** The origin of an audience, or a throw: docs/request.md §3.1. */
|
|
73
|
+
export declare function requestOrigin(audience: string): string;
|
|
74
|
+
/** The RFC 9421 signature base: one `"<name>": <value>` line per covered component, then
|
|
75
|
+
* `"@signature-params": <inner list>` with no trailing line feed (docs/request.md §4). */
|
|
76
|
+
export declare function requestSignatureBase(components: ReadonlyArray<readonly [string, string]>, innerList: string): string;
|
|
77
|
+
/** The base and headers for `input`, for a signer whose key is `publicKey`. Pure; throws on any
|
|
78
|
+
* value outside docs/request.md §3.1. */
|
|
79
|
+
export declare function prepareRequest(publicKey: Uint8Array, input: RequestToSign): PreparedRequest;
|
|
80
|
+
/** The headers, from a signature over a prepared request: checked against it first. Pure. */
|
|
81
|
+
export declare function completeRequest(prepared: PreparedRequest, signature: Uint8Array): RequestHeaders;
|
|
82
|
+
/** Sign `input` with the key behind `seed`: the base, and the four headers to send. */
|
|
83
|
+
export declare function signRequest(seed: Uint8Array, input: RequestToSign): {
|
|
84
|
+
base: string;
|
|
85
|
+
headers: RequestHeaders;
|
|
86
|
+
};
|
|
87
|
+
/** `signRequest` through a signer instead of a seed. */
|
|
88
|
+
export declare function signRequestWith(signer: Signer, input: RequestToSign, signal?: AbortSignal): Promise<RequestHeaders>;
|
|
89
|
+
/**
|
|
90
|
+
* Verify `received` under `policy` at `now` (seconds since the epoch): docs/request.md §7 steps
|
|
91
|
+
* 1–7. Throws on any refusal; returns what was verified. It does not check replay: a server
|
|
92
|
+
* adapter must insert (REQUEST_TAG, audience, principal, nonce) into its replay store before the
|
|
93
|
+
* request reaches an application.
|
|
94
|
+
*/
|
|
95
|
+
export declare function verifyRequest(policy: RequestPolicy, now: number, received: ReceivedRequest): VerifiedRequest;
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
// Request authentication — THIS KEY MADE THIS HTTP REQUEST (ADR 0010 §2–§6; docs/request.md
|
|
2
|
+
// §3–§5 and §7, version 1, fixed by ADR 0010's status note of 4 October 2026).
|
|
3
|
+
//
|
|
4
|
+
// archon's RFC 9421 application profile: the client signs the RFC 9421 signature base directly,
|
|
5
|
+
// in domain REQUEST_DOMAIN, with archon's construction (Ed25519ph with the domain as the RFC 8032
|
|
6
|
+
// context), and `alg` is never sent — the registered `ed25519` is pure Ed25519. The coverage is
|
|
7
|
+
// fixed: the method, the full target URI, the configured audience as a CHECKED ECHO, a SHA-256
|
|
8
|
+
// Content-Digest, Content-Type if present, and every product-declared header the request carries.
|
|
9
|
+
//
|
|
10
|
+
// This file is the sdk's part (ADR 0010 §1): the transcript, the strict parsing, the coverage
|
|
11
|
+
// rule and pure verification. Time is an argument, as everywhere in this package. What it does
|
|
12
|
+
// NOT do is remember: one-use enforcement (the replay store) and the HTTP extraction belong to the
|
|
13
|
+
// server adapters, so a VerifiedRequest is a proof that checked out, not yet a request that may
|
|
14
|
+
// reach an application.
|
|
15
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
16
|
+
import { decodeKey, encodeKey, getPublicKey, signInDomain, verifyInDomain, PUBLIC_KEY_SIZE } from "@bitspark/archon";
|
|
17
|
+
import { checkSignature, signWith } from "./signer.js";
|
|
18
|
+
/** The RFC 8032 context every request signature is made in. */
|
|
19
|
+
export const REQUEST_DOMAIN = "archon-request/1";
|
|
20
|
+
/** The `tag` signature parameter; a verifier accepts only this. */
|
|
21
|
+
export const REQUEST_TAG = "archon-request/1";
|
|
22
|
+
/** The one dictionary member in Signature-Input and Signature. */
|
|
23
|
+
export const REQUEST_LABEL = "archon";
|
|
24
|
+
/** The nonce's least and greatest size, in bytes. */
|
|
25
|
+
export const REQUEST_MIN_NONCE_SIZE = 16;
|
|
26
|
+
export const REQUEST_MAX_NONCE_SIZE = 64;
|
|
27
|
+
/** The longest request-target, and the longest covered value, in bytes. */
|
|
28
|
+
export const REQUEST_MAX_TARGET_SIZE = 8192;
|
|
29
|
+
export const REQUEST_MAX_VALUE_SIZE = 8192;
|
|
30
|
+
/** The largest integer the profile spells: 15 digits. */
|
|
31
|
+
export const REQUEST_MAX_INT = 999_999_999_999_999;
|
|
32
|
+
const utf8 = new TextEncoder();
|
|
33
|
+
const TCHAR = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
34
|
+
const VISIBLE = /^[\x21-\x7e]+$/;
|
|
35
|
+
const RESERVED = new Set(["archon-audience", "content-digest", "content-type", "signature-input", "signature"]);
|
|
36
|
+
/** The origin of an audience, or a throw: docs/request.md §3.1. */
|
|
37
|
+
export function requestOrigin(audience) {
|
|
38
|
+
if (!VISIBLE.test(audience))
|
|
39
|
+
throw new Error("request: the audience is not visible ASCII");
|
|
40
|
+
const m = /^(https?:\/\/)([^/]*)(\/.*)?$/.exec(audience);
|
|
41
|
+
if (m === null)
|
|
42
|
+
throw new Error("request: the audience is not an http or https URL");
|
|
43
|
+
const authority = m[2];
|
|
44
|
+
if (authority === "" || authority.includes("@"))
|
|
45
|
+
throw new Error("request: the audience's authority is empty or carries userinfo");
|
|
46
|
+
if (/[?#]/.test(audience))
|
|
47
|
+
throw new Error("request: the audience carries a query or a fragment");
|
|
48
|
+
if (audience.endsWith("/"))
|
|
49
|
+
throw new Error("request: the audience ends in /");
|
|
50
|
+
return m[1] + authority;
|
|
51
|
+
}
|
|
52
|
+
function checkMethod(method) {
|
|
53
|
+
if (!TCHAR.test(method))
|
|
54
|
+
throw new Error("request: the method is not a token");
|
|
55
|
+
}
|
|
56
|
+
function checkTarget(target) {
|
|
57
|
+
if (target.length === 0 || target.length > REQUEST_MAX_TARGET_SIZE)
|
|
58
|
+
throw new Error("request: the request-target's length");
|
|
59
|
+
if (!target.startsWith("/") || !VISIBLE.test(target) || target.includes("#")) {
|
|
60
|
+
throw new Error("request: the request-target is not origin form");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** A covered value, trimmed of leading and trailing SP and HTAB, or a throw. */
|
|
64
|
+
function coveredValue(value) {
|
|
65
|
+
const v = value.replace(/^[ \t]+|[ \t]+$/g, "");
|
|
66
|
+
if (v.length === 0 || v.length > REQUEST_MAX_VALUE_SIZE || !/^[\t\x20-\x7e]+$/.test(v)) {
|
|
67
|
+
throw new Error("request: a covered value is empty, too long, or not visible ASCII, SP and HTAB");
|
|
68
|
+
}
|
|
69
|
+
return v;
|
|
70
|
+
}
|
|
71
|
+
function checkDeclaredName(name) {
|
|
72
|
+
if (!/^[a-z0-9-]+$/.test(name) || RESERVED.has(name))
|
|
73
|
+
throw new Error(`request: ${JSON.stringify(name)} cannot be a declared header`);
|
|
74
|
+
}
|
|
75
|
+
function checkInt(n, what) {
|
|
76
|
+
if (!Number.isInteger(n) || n < 0 || n > REQUEST_MAX_INT)
|
|
77
|
+
throw new Error(`request: ${what} is not an integer in 0..=${REQUEST_MAX_INT}`);
|
|
78
|
+
}
|
|
79
|
+
function checkNonce(nonce) {
|
|
80
|
+
if (nonce.length < REQUEST_MIN_NONCE_SIZE || nonce.length > REQUEST_MAX_NONCE_SIZE) {
|
|
81
|
+
throw new Error(`request: the nonce is ${nonce.length} bytes, want ${REQUEST_MIN_NONCE_SIZE}..=${REQUEST_MAX_NONCE_SIZE}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** The RFC 9421 signature base: one `"<name>": <value>` line per covered component, then
|
|
85
|
+
* `"@signature-params": <inner list>` with no trailing line feed (docs/request.md §4). */
|
|
86
|
+
export function requestSignatureBase(components, innerList) {
|
|
87
|
+
return components.map(([n, v]) => `"${n}": ${v}\n`).join("") + `"@signature-params": ${innerList}`;
|
|
88
|
+
}
|
|
89
|
+
function contentDigestHeader(body) {
|
|
90
|
+
return `sha-256=:${b64Encode(sha256(body), B64, true)}:`;
|
|
91
|
+
}
|
|
92
|
+
/** The base and headers for `input`, for a signer whose key is `publicKey`. Pure; throws on any
|
|
93
|
+
* value outside docs/request.md §3.1. */
|
|
94
|
+
export function prepareRequest(publicKey, input) {
|
|
95
|
+
if (publicKey.length !== PUBLIC_KEY_SIZE)
|
|
96
|
+
throw new Error("request: the public key is not 32 bytes");
|
|
97
|
+
checkMethod(input.method);
|
|
98
|
+
const origin = requestOrigin(input.audience);
|
|
99
|
+
checkTarget(input.requestTarget);
|
|
100
|
+
checkInt(input.created, "created");
|
|
101
|
+
checkInt(input.expires, "expires");
|
|
102
|
+
if (input.created >= input.expires)
|
|
103
|
+
throw new Error("request: created must be before expires");
|
|
104
|
+
checkNonce(input.nonce);
|
|
105
|
+
const digest = contentDigestHeader(input.body);
|
|
106
|
+
const components = [
|
|
107
|
+
["@method", input.method],
|
|
108
|
+
["@target-uri", origin + input.requestTarget],
|
|
109
|
+
["archon-audience", input.audience],
|
|
110
|
+
["content-digest", digest],
|
|
111
|
+
];
|
|
112
|
+
if (input.contentType !== undefined)
|
|
113
|
+
components.push(["content-type", coveredValue(input.contentType)]);
|
|
114
|
+
const seen = new Set();
|
|
115
|
+
for (const [name, value] of input.declared) {
|
|
116
|
+
checkDeclaredName(name);
|
|
117
|
+
if (seen.has(name))
|
|
118
|
+
throw new Error(`request: ${name} is declared twice`);
|
|
119
|
+
seen.add(name);
|
|
120
|
+
components.push([name, coveredValue(value)]);
|
|
121
|
+
}
|
|
122
|
+
const keyText = encodeKey(publicKey);
|
|
123
|
+
const inner = `(${components.map(([n]) => `"${n}"`).join(" ")});created=${input.created};expires=${input.expires}` +
|
|
124
|
+
`;nonce="${b64Encode(input.nonce, B64URL, false)}";keyid="${keyText}";tag="${REQUEST_TAG}"`;
|
|
125
|
+
const base = requestSignatureBase(components, inner);
|
|
126
|
+
return {
|
|
127
|
+
base,
|
|
128
|
+
signing: { expectedPublicKey: publicKey, scheme: { kind: "ed25519ph-context", domain: REQUEST_DOMAIN }, message: utf8.encode(base) },
|
|
129
|
+
headers: { "archon-audience": input.audience, "content-digest": digest, "signature-input": `${REQUEST_LABEL}=${inner}` },
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** The headers, from a signature over a prepared request: checked against it first. Pure. */
|
|
133
|
+
export function completeRequest(prepared, signature) {
|
|
134
|
+
checkSignature(prepared.signing, signature);
|
|
135
|
+
return { ...prepared.headers, signature: `${REQUEST_LABEL}=:${b64Encode(signature, B64, true)}:` };
|
|
136
|
+
}
|
|
137
|
+
/** Sign `input` with the key behind `seed`: the base, and the four headers to send. */
|
|
138
|
+
export function signRequest(seed, input) {
|
|
139
|
+
const prepared = prepareRequest(getPublicKey(seed), input);
|
|
140
|
+
const signature = signInDomain(seed, REQUEST_DOMAIN, prepared.signing.message);
|
|
141
|
+
return { base: prepared.base, headers: completeRequest(prepared, signature) };
|
|
142
|
+
}
|
|
143
|
+
/** `signRequest` through a signer instead of a seed. */
|
|
144
|
+
export async function signRequestWith(signer, input, signal) {
|
|
145
|
+
const prepared = prepareRequest(signer.publicKey, input);
|
|
146
|
+
return completeRequest(prepared, await signWith(signer, prepared.signing, signal));
|
|
147
|
+
}
|
|
148
|
+
const SIGNATURE_INPUT = /^archon=\(("[^"]*"(?: "[^"]*")*)\);created=(0|[1-9][0-9]{0,14});expires=(0|[1-9][0-9]{0,14});nonce="([A-Za-z0-9_-]+)";keyid="([^"\\]*)";tag="archon-request\/1"$/;
|
|
149
|
+
const SIGNATURE = /^archon=:([A-Za-z0-9+/=]+):$/;
|
|
150
|
+
const CONTENT_DIGEST = /^sha-256=:([A-Za-z0-9+/=]+):$/;
|
|
151
|
+
/**
|
|
152
|
+
* Verify `received` under `policy` at `now` (seconds since the epoch): docs/request.md §7 steps
|
|
153
|
+
* 1–7. Throws on any refusal; returns what was verified. It does not check replay: a server
|
|
154
|
+
* adapter must insert (REQUEST_TAG, audience, principal, nonce) into its replay store before the
|
|
155
|
+
* request reaches an application.
|
|
156
|
+
*/
|
|
157
|
+
export function verifyRequest(policy, now, received) {
|
|
158
|
+
const origin = requestOrigin(policy.audience);
|
|
159
|
+
const declared = new Set();
|
|
160
|
+
for (const d of policy.declared) {
|
|
161
|
+
checkDeclaredName(d);
|
|
162
|
+
if (declared.has(d))
|
|
163
|
+
throw new Error(`request: ${d} is declared twice`);
|
|
164
|
+
declared.add(d);
|
|
165
|
+
}
|
|
166
|
+
checkInt(policy.maxLifetime, "max_lifetime");
|
|
167
|
+
if (policy.maxLifetime < 1)
|
|
168
|
+
throw new Error("request: max_lifetime must be at least 1");
|
|
169
|
+
checkInt(policy.skew, "skew");
|
|
170
|
+
checkInt(now, "now");
|
|
171
|
+
const fields = new Map();
|
|
172
|
+
for (const [name, value] of received.headers) {
|
|
173
|
+
// ASCII case folding only: HTTP field names are tokens, and Unicode folding would turn
|
|
174
|
+
// U+212A KELVIN SIGN into `k` — a different header in a lane that folds ASCII only.
|
|
175
|
+
const key = name.replace(/[A-Z]/g, (ch) => ch.toLowerCase());
|
|
176
|
+
const list = fields.get(key) ?? [];
|
|
177
|
+
list.push(value.replace(/^[ \t]+|[ \t]+$/g, ""));
|
|
178
|
+
fields.set(key, list);
|
|
179
|
+
}
|
|
180
|
+
const once = (name) => {
|
|
181
|
+
const list = fields.get(name);
|
|
182
|
+
if (list === undefined || list.length !== 1)
|
|
183
|
+
throw new Error(`request: ${name} must appear exactly once`);
|
|
184
|
+
return list[0];
|
|
185
|
+
};
|
|
186
|
+
// 1. The two proof fields parse, in their one spelling.
|
|
187
|
+
const si = SIGNATURE_INPUT.exec(once("signature-input"));
|
|
188
|
+
if (si === null)
|
|
189
|
+
throw new Error("request: Signature-Input is not the profile's spelling");
|
|
190
|
+
const sigText = SIGNATURE.exec(once("signature"));
|
|
191
|
+
if (sigText === null)
|
|
192
|
+
throw new Error("request: Signature is not the profile's spelling");
|
|
193
|
+
const signature = b64Decode(sigText[1], B64, true);
|
|
194
|
+
if (signature.length !== 64)
|
|
195
|
+
throw new Error("request: the signature is not 64 bytes");
|
|
196
|
+
const names = si[1].split(" ").map((q) => q.slice(1, -1));
|
|
197
|
+
const created = Number(si[2]);
|
|
198
|
+
const expires = Number(si[3]);
|
|
199
|
+
const nonce = b64Decode(si[4], B64URL, false);
|
|
200
|
+
checkNonce(nonce);
|
|
201
|
+
// 2. keyid is a canonical principal (the tag was matched by the grammar).
|
|
202
|
+
const keyText = si[5];
|
|
203
|
+
let principal;
|
|
204
|
+
try {
|
|
205
|
+
principal = decodeKey(keyText);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
throw new Error("request: keyid is not a principal");
|
|
209
|
+
}
|
|
210
|
+
if (encodeKey(principal) !== keyText)
|
|
211
|
+
throw new Error("request: keyid is not the canonical key text");
|
|
212
|
+
// 3. The coverage is exactly §3.1's, for what this request carries.
|
|
213
|
+
const expected = ["@method", "@target-uri", "archon-audience", "content-digest"];
|
|
214
|
+
if (fields.has("content-type"))
|
|
215
|
+
expected.push("content-type");
|
|
216
|
+
for (const d of policy.declared)
|
|
217
|
+
if (fields.has(d))
|
|
218
|
+
expected.push(d);
|
|
219
|
+
if (names.length !== expected.length || names.some((n, i) => n !== expected[i])) {
|
|
220
|
+
throw new Error("request: the coverage is not the profile's for this request");
|
|
221
|
+
}
|
|
222
|
+
checkMethod(received.method);
|
|
223
|
+
checkTarget(received.requestTarget);
|
|
224
|
+
const components = [];
|
|
225
|
+
for (const n of names) {
|
|
226
|
+
if (n === "@method")
|
|
227
|
+
components.push([n, received.method]);
|
|
228
|
+
else if (n === "@target-uri")
|
|
229
|
+
components.push([n, origin + received.requestTarget]);
|
|
230
|
+
else
|
|
231
|
+
components.push([n, coveredValue(once(n))]);
|
|
232
|
+
}
|
|
233
|
+
// 4. The audience echo is the configured audience, byte for byte.
|
|
234
|
+
if (once("archon-audience") !== policy.audience)
|
|
235
|
+
throw new Error("request: Archon-Audience is not the configured audience");
|
|
236
|
+
// 5. The digest is the received content's.
|
|
237
|
+
const cd = CONTENT_DIGEST.exec(once("content-digest"));
|
|
238
|
+
if (cd === null)
|
|
239
|
+
throw new Error("request: Content-Digest is not the profile's spelling");
|
|
240
|
+
const contentDigest = b64Decode(cd[1], B64, true);
|
|
241
|
+
const actual = sha256(received.body);
|
|
242
|
+
if (contentDigest.length !== actual.length || contentDigest.some((b, i) => b !== actual[i])) {
|
|
243
|
+
throw new Error("request: Content-Digest does not match the content");
|
|
244
|
+
}
|
|
245
|
+
// 6. Freshness: 0 < e − c ≤ W and c − δ ≤ now < e + δ.
|
|
246
|
+
if (!(created < expires && expires - created <= policy.maxLifetime))
|
|
247
|
+
throw new Error("request: the lifetime is out of bounds");
|
|
248
|
+
if (!(created - policy.skew <= now && now < expires + policy.skew))
|
|
249
|
+
throw new Error("request: not fresh at now");
|
|
250
|
+
// 7. The signature, over the base built from what was received and configured.
|
|
251
|
+
const base = requestSignatureBase(components, si[0].slice(REQUEST_LABEL.length + 1));
|
|
252
|
+
if (!verifyInDomain(principal, REQUEST_DOMAIN, utf8.encode(base), signature))
|
|
253
|
+
throw new Error("request: the signature does not verify");
|
|
254
|
+
return {
|
|
255
|
+
principal,
|
|
256
|
+
keyText,
|
|
257
|
+
created,
|
|
258
|
+
expires,
|
|
259
|
+
nonce,
|
|
260
|
+
method: received.method,
|
|
261
|
+
targetUri: origin + received.requestTarget,
|
|
262
|
+
covered: components.filter(([n]) => !n.startsWith("@")),
|
|
263
|
+
contentDigest,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
// ---------------------------------------------------------------------------------------------
|
|
267
|
+
// base64, both alphabets, accepted only in their one canonical spelling (docs/request.md §3.1):
|
|
268
|
+
// decoding and re-encoding must give back the same text.
|
|
269
|
+
// ---------------------------------------------------------------------------------------------
|
|
270
|
+
const B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
271
|
+
const B64URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
272
|
+
function b64Encode(bytes, alphabet, pad) {
|
|
273
|
+
let out = "";
|
|
274
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
275
|
+
const n = (bytes[i] << 16) | ((bytes[i + 1] ?? 0) << 8) | (bytes[i + 2] ?? 0);
|
|
276
|
+
const chars = Math.min(4, Math.ceil(((bytes.length - i) * 8) / 6));
|
|
277
|
+
for (let j = 0; j < chars; j++)
|
|
278
|
+
out += alphabet[(n >> (18 - 6 * j)) & 63];
|
|
279
|
+
if (pad)
|
|
280
|
+
out += "=".repeat(4 - chars);
|
|
281
|
+
}
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
function b64Decode(text, alphabet, pad) {
|
|
285
|
+
const body = pad ? text.replace(/=+$/, "") : text;
|
|
286
|
+
const bytes = [];
|
|
287
|
+
let acc = 0;
|
|
288
|
+
let bits = 0;
|
|
289
|
+
for (const ch of body) {
|
|
290
|
+
const v = alphabet.indexOf(ch);
|
|
291
|
+
if (v < 0)
|
|
292
|
+
throw new Error("request: not base64");
|
|
293
|
+
acc = (acc << 6) | v;
|
|
294
|
+
bits += 6;
|
|
295
|
+
if (bits >= 8) {
|
|
296
|
+
bits -= 8;
|
|
297
|
+
bytes.push((acc >> bits) & 0xff);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const out = new Uint8Array(bytes);
|
|
301
|
+
if (b64Encode(out, alphabet, pad) !== text)
|
|
302
|
+
throw new Error("request: not the canonical base64 spelling");
|
|
303
|
+
return out;
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/request.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Raw Ed25519 over the message, or Ed25519ph with the domain as the RFC 8032 context. */
|
|
2
|
+
export type SigningScheme = {
|
|
3
|
+
readonly kind: "ed25519-raw";
|
|
4
|
+
} | {
|
|
5
|
+
readonly kind: "ed25519ph-context";
|
|
6
|
+
readonly domain: string;
|
|
7
|
+
};
|
|
8
|
+
/** What a signer is asked to sign. `message` is always the original bytes. */
|
|
9
|
+
export interface SigningRequest {
|
|
10
|
+
readonly expectedPublicKey: Uint8Array;
|
|
11
|
+
readonly scheme: SigningScheme;
|
|
12
|
+
readonly message: Uint8Array;
|
|
13
|
+
}
|
|
14
|
+
/** What a signer can do. `domains`, when present, is the only set it will sign in. */
|
|
15
|
+
export interface SignerCapabilities {
|
|
16
|
+
readonly schemes: readonly SigningScheme["kind"][];
|
|
17
|
+
readonly domains?: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A source of signatures. `sign` may prompt, call a subprocess or wait on a device; it is
|
|
21
|
+
* given the abort signal, and a signature it returns after the signal fired is discarded,
|
|
22
|
+
* never packaged.
|
|
23
|
+
*/
|
|
24
|
+
export interface Signer {
|
|
25
|
+
readonly publicKey: Uint8Array;
|
|
26
|
+
readonly capabilities: SignerCapabilities;
|
|
27
|
+
sign(request: SigningRequest, signal?: AbortSignal): Promise<Uint8Array>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Throws unless `request` is in range: a 32-byte expected key, a known scheme, and — for
|
|
31
|
+
* Ed25519ph — a domain the floor accepts. The domain is checked by the floor's own rule (it
|
|
32
|
+
* signs nothing with a throwaway key), so no copy of ADR 0008 §2 lives here to drift.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateSigningRequest(request: SigningRequest): void;
|
|
35
|
+
/**
|
|
36
|
+
* Throws unless `signature` verifies for exactly what was REQUESTED: its key, scheme, domain
|
|
37
|
+
* and original bytes.
|
|
38
|
+
*/
|
|
39
|
+
export declare function checkSignature(request: SigningRequest, signature: Uint8Array): void;
|
|
40
|
+
/**
|
|
41
|
+
* Throws unless `signer` claims to be able to sign `request`: its key is the expected one,
|
|
42
|
+
* and the scheme and domain are among its capabilities. Called before it is invoked.
|
|
43
|
+
*/
|
|
44
|
+
export declare function checkCapability(signer: Signer, request: SigningRequest): void;
|
|
45
|
+
/**
|
|
46
|
+
* Validates, checks the signer's capabilities, invokes it, and returns the signature only
|
|
47
|
+
* if the call was not aborted and the signature verifies for what was requested.
|
|
48
|
+
*/
|
|
49
|
+
export declare function signWith(signer: Signer, request: SigningRequest, signal?: AbortSignal): Promise<Uint8Array>;
|
|
50
|
+
/**
|
|
51
|
+
* The software signer: a seed held in this process, both schemes, any domain the floor
|
|
52
|
+
* accepts, deterministic by the floor's construction. The seed is copied.
|
|
53
|
+
*/
|
|
54
|
+
export declare function seedSigner(seed: Uint8Array): Signer;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// The signer contract — SIGNING CAPABILITY, NOT SEEDS (ADR 0009 §2–4).
|
|
2
|
+
//
|
|
3
|
+
// Every helper in this package can take a seed, which is right for a key held in software
|
|
4
|
+
// and wrong for one that is not: a stored key behind `archon sign --key`, or any backend a
|
|
5
|
+
// caller wires in. So each signing helper also comes in three parts — a PURE prepare (the
|
|
6
|
+
// exact request it needs signed), the signer's own work, and a PURE complete (the checked
|
|
7
|
+
// signature, then packaging) — with a convenience that runs the signer between them. The
|
|
8
|
+
// seed functions are unchanged and stay synchronous.
|
|
9
|
+
//
|
|
10
|
+
// A request is three things, and the scheme is a discriminated value, never a scheme name
|
|
11
|
+
// plus an optional domain: the public key the caller expects, raw Ed25519 or Ed25519ph with
|
|
12
|
+
// the domain as the RFC 8032 context, and the ORIGINAL message bytes — never a digest; a
|
|
13
|
+
// backend that wants a prehash computes it inside its own adapter.
|
|
14
|
+
//
|
|
15
|
+
// Three rules, all enforced here rather than trusted to a signer:
|
|
16
|
+
//
|
|
17
|
+
// - A request is validated BEFORE the signer is invoked: a wrong-length key or a domain the
|
|
18
|
+
// floor refuses never reaches it.
|
|
19
|
+
// - A signer REPORTS what it can do, and a request outside that is refused before it is
|
|
20
|
+
// invoked. A signer that cannot carry a context declares raw only; it must never sign an
|
|
21
|
+
// Ed25519ph request with an empty context instead, and if it does anyway, the check below
|
|
22
|
+
// catches it.
|
|
23
|
+
// - Every returned signature is VERIFIED against the requested key, scheme, domain and bytes
|
|
24
|
+
// — never against values the signer echoes back. That catches a wrong key, a dropped
|
|
25
|
+
// context, raw substituted for ph, and a wrong prehash adaptation. It does not prove that a
|
|
26
|
+
// signer signs deterministically (ADR 0008 §1.7 requires it); that is a property of the
|
|
27
|
+
// backend, tested against the `domain_sign` vectors, not something one signature can show.
|
|
28
|
+
//
|
|
29
|
+
// Domain separation is not authorization: whoever may ask for signatures in a domain gets
|
|
30
|
+
// any signature in that domain. Consent belongs to the caller that knows what the bytes mean.
|
|
31
|
+
import { getPublicKey, sign, signInDomain, verify, verifyInDomain, PUBLIC_KEY_SIZE, SEED_SIZE, SIGNATURE_SIZE, } from "@bitspark/archon";
|
|
32
|
+
const sameBytes = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
|
|
33
|
+
/**
|
|
34
|
+
* Throws unless `request` is in range: a 32-byte expected key, a known scheme, and — for
|
|
35
|
+
* Ed25519ph — a domain the floor accepts. The domain is checked by the floor's own rule (it
|
|
36
|
+
* signs nothing with a throwaway key), so no copy of ADR 0008 §2 lives here to drift.
|
|
37
|
+
*/
|
|
38
|
+
export function validateSigningRequest(request) {
|
|
39
|
+
if (!(request.expectedPublicKey instanceof Uint8Array) || request.expectedPublicKey.length !== PUBLIC_KEY_SIZE) {
|
|
40
|
+
throw new Error(`signer: the expected public key must be ${PUBLIC_KEY_SIZE} bytes`);
|
|
41
|
+
}
|
|
42
|
+
if (!(request.message instanceof Uint8Array))
|
|
43
|
+
throw new Error("signer: the message must be bytes");
|
|
44
|
+
const scheme = request.scheme;
|
|
45
|
+
if (scheme.kind === "ed25519-raw") {
|
|
46
|
+
if ("domain" in scheme)
|
|
47
|
+
throw new Error("signer: a raw request carries no domain");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (scheme.kind !== "ed25519ph-context")
|
|
51
|
+
throw new Error(`signer: unknown scheme ${JSON.stringify(scheme.kind)}`);
|
|
52
|
+
if (typeof scheme.domain !== "string")
|
|
53
|
+
throw new Error("signer: an ed25519ph-context request needs a domain");
|
|
54
|
+
signInDomain(new Uint8Array(SEED_SIZE), scheme.domain, new Uint8Array(0));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Throws unless `signature` verifies for exactly what was REQUESTED: its key, scheme, domain
|
|
58
|
+
* and original bytes.
|
|
59
|
+
*/
|
|
60
|
+
export function checkSignature(request, signature) {
|
|
61
|
+
validateSigningRequest(request);
|
|
62
|
+
const ok = signature instanceof Uint8Array &&
|
|
63
|
+
signature.length === SIGNATURE_SIZE &&
|
|
64
|
+
(request.scheme.kind === "ed25519-raw"
|
|
65
|
+
? verify(signature, request.message, request.expectedPublicKey)
|
|
66
|
+
: verifyInDomain(request.expectedPublicKey, request.scheme.domain, request.message, signature));
|
|
67
|
+
if (!ok) {
|
|
68
|
+
throw new Error("signer: the signature does not verify for the requested key, scheme and message");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Throws unless `signer` claims to be able to sign `request`: its key is the expected one,
|
|
73
|
+
* and the scheme and domain are among its capabilities. Called before it is invoked.
|
|
74
|
+
*/
|
|
75
|
+
export function checkCapability(signer, request) {
|
|
76
|
+
if (!sameBytes(signer.publicKey, request.expectedPublicKey)) {
|
|
77
|
+
throw new Error("signer: this signer's key is not the expected key");
|
|
78
|
+
}
|
|
79
|
+
const caps = signer.capabilities;
|
|
80
|
+
if (!caps.schemes.includes(request.scheme.kind)) {
|
|
81
|
+
throw new Error(`signer: this signer cannot produce ${request.scheme.kind}`);
|
|
82
|
+
}
|
|
83
|
+
if (request.scheme.kind === "ed25519ph-context" && caps.domains !== undefined && !caps.domains.includes(request.scheme.domain)) {
|
|
84
|
+
throw new Error(`signer: this signer does not sign in domain ${JSON.stringify(request.scheme.domain)}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validates, checks the signer's capabilities, invokes it, and returns the signature only
|
|
89
|
+
* if the call was not aborted and the signature verifies for what was requested.
|
|
90
|
+
*/
|
|
91
|
+
export async function signWith(signer, request, signal) {
|
|
92
|
+
validateSigningRequest(request);
|
|
93
|
+
checkCapability(signer, request);
|
|
94
|
+
signal?.throwIfAborted();
|
|
95
|
+
const signature = await signer.sign(request, signal);
|
|
96
|
+
signal?.throwIfAborted();
|
|
97
|
+
checkSignature(request, signature);
|
|
98
|
+
return new Uint8Array(signature);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The software signer: a seed held in this process, both schemes, any domain the floor
|
|
102
|
+
* accepts, deterministic by the floor's construction. The seed is copied.
|
|
103
|
+
*/
|
|
104
|
+
export function seedSigner(seed) {
|
|
105
|
+
if (seed.length !== SEED_SIZE)
|
|
106
|
+
throw new Error(`signer: seed is ${seed.length} bytes, want ${SEED_SIZE}`);
|
|
107
|
+
const held = new Uint8Array(seed);
|
|
108
|
+
return {
|
|
109
|
+
publicKey: getPublicKey(held),
|
|
110
|
+
capabilities: { schemes: ["ed25519-raw", "ed25519ph-context"] },
|
|
111
|
+
sign: (request) => Promise.resolve(request.scheme.kind === "ed25519-raw"
|
|
112
|
+
? sign(request.message, held)
|
|
113
|
+
: signInDomain(held, request.scheme.domain, request.message)),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=signer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signer.js","sourceRoot":"","sources":["../../src/signer.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitspark/archon-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "archon-sdk: proof of possession and the signed envelope, one layer above the identity floor. Deterministic given its inputs; no RNG, no clock, no socket.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"test": "tsc -p tsconfig.json && node --test dist/test/*.test.js"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@bitspark/archon": "^0.
|
|
25
|
+
"@bitspark/archon": "^0.10.0",
|
|
26
|
+
"@noble/hashes": "^2.2.0"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"@types/node": "^26",
|