@kindgi/crypto 0.1.4 → 0.1.5-rc.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 CHANGED
@@ -105,6 +105,30 @@ if (priv === null) throw new Error('key not provisioned');
105
105
 
106
106
  `createInMemorySigningKeyBinding` is for tests and local development. Production deployments implement the interface against their own key store (a cloud KMS, a secrets vault, an HSM). This package never persists key material.
107
107
 
108
+ ## ExportSigningBinding
109
+
110
+ The key a deployment signs its exports with (audit bundles, provenance, compliance evidence). It's async, so a KMS that never hands out its private key can implement it, as well as a key file can:
111
+
112
+ ```ts
113
+ import { createExportSignerFromPem } from '@kindgi/crypto';
114
+
115
+ // An Ed25519 key signs `ed25519`; an EC P-256 key signs `ecdsa-p256-sha256`.
116
+ const made = createExportSignerFromPem(await readFile(keyPath, 'utf8'));
117
+ if (made.kind === 'err') throw new Error(made.error.message);
118
+ const signer = made.value;
119
+
120
+ signer.activeKey(); // { keyId: 'ex_…', algorithm: 'ed25519', publicKeyPem, fingerprint: 'sha256:…' }
121
+ const signed = await signer.sign(bytes); // the active key, or { keyId }
122
+ ```
123
+
124
+ - **Key ids are derived from the public key** (`exportSigningKey`), so the same key keeps its id.
125
+ - **`listKeys()`** is every key a verifier should trust, active first.
126
+ - **`exportSignerFromSigningKeyBinding`** adapts a `SigningKeyBinding`'s Ed25519 keys.
127
+ - **Two algorithms, chosen per key:** `ed25519` (the default) and `ecdsa-p256-sha256`, for a KMS without Ed25519. An ECDSA signature is IEEE P1363 `r‖s`, 64 bytes. A KMS binding that gets DER back (Cloud KMS, AWS KMS) converts with `ecdsaDerToP1363`.
128
+ - **Test vectors** for both are in `@kindgi/specs` (`test-vectors/signed-export/`).
129
+
130
+ `@kindgi/api` takes one as `createApp({ exportSigning })`. To verify an export, use `verifySignedExport` from `@kindgi/client` (Web Crypto, so it runs in a browser too).
131
+
108
132
  ## Security notes
109
133
 
110
134
  - **Never log private keys or shared secrets.** Journals, provenance blobs, structured logs, and wire payloads must not contain key material except by explicit caller intent.
@@ -0,0 +1,91 @@
1
+ import type { Result } from '@kindgi/types';
2
+ import type { SigningKeyBinding } from './binding.js';
3
+ import type { CryptoError } from './errors.js';
4
+ /** The algorithms an export can be signed with. */
5
+ export declare const EXPORT_SIGNING_ALGORITHMS: readonly ["ed25519", "ecdsa-p256-sha256"];
6
+ export type ExportSigningAlgorithm = (typeof EXPORT_SIGNING_ALGORITHMS)[number];
7
+ /** A key exports are signed with, as a verifier sees it. No private material. */
8
+ export interface ExportSigningKey {
9
+ /**
10
+ * `ex_` and 16 base64url characters of the SHA-256 of the raw public
11
+ * key: Ed25519's 32 bytes, P-256's 65-byte uncompressed point.
12
+ */
13
+ readonly keyId: string;
14
+ readonly algorithm: ExportSigningAlgorithm;
15
+ /** The public key, PEM SPKI. */
16
+ readonly publicKeyPem: string;
17
+ /** `sha256:` and the hex SHA-256 of the raw public key: to pin it, or compare by eye. */
18
+ readonly fingerprint: string;
19
+ }
20
+ export type ExportSigningError = {
21
+ /** `keyId` was asked for and isn't a key this binding signs with. */
22
+ readonly code: 'signing-key-not-found';
23
+ readonly message: string;
24
+ readonly keyId: string;
25
+ } | {
26
+ /** The signer failed: a KMS that refused or didn't answer, a malformed key. */
27
+ readonly code: 'export-signing-failed';
28
+ readonly message: string;
29
+ readonly cause?: unknown;
30
+ };
31
+ export interface ExportSignature {
32
+ readonly key: ExportSigningKey;
33
+ readonly signature: Uint8Array;
34
+ }
35
+ export interface ExportSigningBinding {
36
+ /** The key `sign` uses when no `keyId` is asked for. */
37
+ activeKey(): ExportSigningKey;
38
+ /** Every key a verifier should trust, the active one first. */
39
+ listKeys(): readonly ExportSigningKey[];
40
+ /**
41
+ * Sign `bytes` with the active key, or with `keyId` when given. A
42
+ * `keyId` this binding can't sign with is `signing-key-not-found`.
43
+ */
44
+ sign(bytes: Uint8Array, options?: {
45
+ readonly keyId?: string;
46
+ }): Promise<Result<ExportSignature, ExportSigningError>>;
47
+ }
48
+ /**
49
+ * The `ExportSigningKey` for a raw public key, under its derived id: a
50
+ * 32-byte Ed25519 key (the default), or a 65-byte uncompressed P-256 point
51
+ * for `ecdsa-p256-sha256`.
52
+ */
53
+ export declare function exportSigningKey(publicKey: Uint8Array, algorithm?: ExportSigningAlgorithm): ExportSigningKey;
54
+ /**
55
+ * An export signer over an EC P-256 private key held in memory (PKCS#8 or
56
+ * SEC1 PEM): `ecdsa-p256-sha256`, its signatures as P1363 `r‖s`. `err`
57
+ * when the key isn't a P-256 private key.
58
+ */
59
+ export declare function createEcdsaP256ExportSigner(input: {
60
+ readonly privateKeyPem: string;
61
+ }): Result<ExportSigningBinding, CryptoError>;
62
+ /**
63
+ * The export signer for a PEM private key, its algorithm read from the
64
+ * key: Ed25519 → `ed25519`, EC P-256 → `ecdsa-p256-sha256`. Any other key
65
+ * is `err`, naming what it is.
66
+ */
67
+ export declare function createExportSignerFromPem(privateKeyPem: string): Result<ExportSigningBinding, CryptoError>;
68
+ /**
69
+ * An ECDSA signature from DER (`SEQUENCE { INTEGER r, INTEGER s }`, what
70
+ * Cloud KMS and AWS KMS answer) to IEEE P1363 `r‖s`, each half `size`
71
+ * bytes (32 for P-256): the form exports carry and Web Crypto verifies.
72
+ */
73
+ export declare function ecdsaDerToP1363(der: Uint8Array, size?: number): Result<Uint8Array, CryptoError>;
74
+ /**
75
+ * An export signer over an Ed25519 private key held in memory: a PEM
76
+ * file's contents, a base64 env value decoded, a test's or a
77
+ * development server's generated key. `err` when the key isn't an
78
+ * Ed25519 PKCS#8 key.
79
+ */
80
+ export declare function createEd25519ExportSigner(input: {
81
+ readonly privateKeyPem: string;
82
+ } | {
83
+ readonly privateKey: Uint8Array;
84
+ }): Result<ExportSigningBinding, CryptoError>;
85
+ /**
86
+ * The `ExportSigningBinding` over a `SigningKeyBinding`'s Ed25519 keys,
87
+ * under the ids that binding gives them; the first is the active one.
88
+ * `undefined` when it holds no Ed25519 key (nothing to sign with).
89
+ */
90
+ export declare function exportSignerFromSigningKeyBinding(binding: SigningKeyBinding): ExportSigningBinding | undefined;
91
+ //# sourceMappingURL=export-signing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export-signing.d.ts","sourceRoot":"","sources":["../src/export-signing.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,mDAAmD;AACnD,eAAO,MAAM,yBAAyB,2CAA4C,CAAC;AACnF,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhF,iFAAiF;AACjF,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,sBAAsB,CAAC;IAC3C,gCAAgC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,yFAAyF;IACzF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,MAAM,kBAAkB,GAC1B;IACE,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,GACD;IACE,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEN,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,gBAAgB,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,SAAS,IAAI,gBAAgB,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,IAAI,SAAS,gBAAgB,EAAE,CAAC;IACxC;;;OAGG;IACH,IAAI,CACF,KAAK,EAAE,UAAU,EACjB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACpC,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAC,CAAC;CACzD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,UAAU,EACrB,SAAS,GAAE,sBAAkC,GAC5C,gBAAgB,CAUlB;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE;IACjD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC,GAAG,MAAM,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAsC5C;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,GACpB,MAAM,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAa3C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,SAAK,GAAG,MAAM,CAAC,UAAU,EAAE,WAAW,CAAC,CAqC3F;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE;IAAE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;CAAE,GAC9E,MAAM,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAkB3C;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,iBAAiB,GACzB,oBAAoB,GAAG,SAAS,CAuBlC"}
@@ -0,0 +1,283 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+ /**
4
+ * `ExportSigningBinding` — the key a deployment signs its exports with:
5
+ * audit bundles, provenance exports and compliance evidence. Async, so
6
+ * a KMS that never hands out its private key (Cloud KMS
7
+ * `asymmetricSign`) can implement it as well as a key file can.
8
+ *
9
+ * One key signs at a time (`activeKey`); `listKeys` is every key a
10
+ * verifier should trust, active first. A key's id is derived from its
11
+ * public key (`exportSigningKey`), so the same key has the same id
12
+ * across restarts and deployments, with nothing to configure.
13
+ *
14
+ * Two algorithms, chosen per key (`ExportSigningKey.algorithm`):
15
+ * `ed25519`, the default for a key in memory or a file, and
16
+ * `ecdsa-p256-sha256`, for a KMS that has no Ed25519. An ECDSA signature
17
+ * travels as IEEE P1363 `r‖s` (64 bytes), the form Web Crypto verifies;
18
+ * a KMS that answers DER converts with `ecdsaDerToP1363`.
19
+ *
20
+ * Not the same seam as `SigningKeyBinding` (raw key bytes, read
21
+ * synchronously, for public run tokens on the request path).
22
+ * `exportSignerFromSigningKeyBinding` adapts one to the other.
23
+ */
24
+ import { createHash, createPrivateKey, createPublicKey, sign as nodeSign, } from 'node:crypto';
25
+ import { parsePrivateKeyPem, serializePublicKeyPem, signEd25519 } from './ed25519.js';
26
+ import { ED25519_RAW_KEY_BYTES, unwrapPublicKeySpki, wrapPrivateKeyPkcs8 } from './encoding.js';
27
+ import { malformedKey } from './errors.js';
28
+ /** The algorithms an export can be signed with. */
29
+ export const EXPORT_SIGNING_ALGORITHMS = ['ed25519', 'ecdsa-p256-sha256'];
30
+ /**
31
+ * The `ExportSigningKey` for a raw public key, under its derived id: a
32
+ * 32-byte Ed25519 key (the default), or a 65-byte uncompressed P-256 point
33
+ * for `ecdsa-p256-sha256`.
34
+ */
35
+ export function exportSigningKey(publicKey, algorithm = 'ed25519') {
36
+ const keyId = `ex_${sha256(publicKey).toString('base64url').slice(0, 16)}`;
37
+ return algorithm === 'ed25519'
38
+ ? describeKey(keyId, publicKey)
39
+ : {
40
+ keyId,
41
+ algorithm,
42
+ publicKeyPem: p256PublicKeyPem(publicKey),
43
+ fingerprint: `sha256:${sha256(publicKey).toString('hex')}`,
44
+ };
45
+ }
46
+ /**
47
+ * An export signer over an EC P-256 private key held in memory (PKCS#8 or
48
+ * SEC1 PEM): `ecdsa-p256-sha256`, its signatures as P1363 `r‖s`. `err`
49
+ * when the key isn't a P-256 private key.
50
+ */
51
+ export function createEcdsaP256ExportSigner(input) {
52
+ let privateKey;
53
+ try {
54
+ privateKey = createPrivateKey({ key: input.privateKeyPem, format: 'pem' });
55
+ }
56
+ catch (cause) {
57
+ return malformedKey('the export signing key is not a readable PEM private key', cause);
58
+ }
59
+ const curve = privateKey.asymmetricKeyDetails?.namedCurve;
60
+ if (privateKey.asymmetricKeyType !== 'ec' || curve !== 'prime256v1') {
61
+ return malformedKey(`the export signing key is ${describeKeyType(privateKey)}, not an EC P-256 key`);
62
+ }
63
+ const jwk = createPublicKey(privateKey).export({ format: 'jwk' });
64
+ const point = Buffer.concat([
65
+ Buffer.from([0x04]),
66
+ Buffer.from(jwk.x, 'base64url'),
67
+ Buffer.from(jwk.y, 'base64url'),
68
+ ]);
69
+ const key = exportSigningKey(new Uint8Array(point), 'ecdsa-p256-sha256');
70
+ return ok({
71
+ activeKey: () => key,
72
+ listKeys: () => [key],
73
+ async sign(bytes, options) {
74
+ if (options?.keyId !== undefined && options.keyId !== key.keyId) {
75
+ return { kind: 'err', error: keyNotFound(options.keyId, [key]) };
76
+ }
77
+ try {
78
+ const signature = nodeSign('sha256', bytes, { key: privateKey, dsaEncoding: 'ieee-p1363' });
79
+ return ok({ key, signature: new Uint8Array(signature) });
80
+ }
81
+ catch (cause) {
82
+ return {
83
+ kind: 'err',
84
+ error: { code: 'export-signing-failed', message: 'ECDSA P-256 signing failed', cause },
85
+ };
86
+ }
87
+ },
88
+ });
89
+ }
90
+ /**
91
+ * The export signer for a PEM private key, its algorithm read from the
92
+ * key: Ed25519 → `ed25519`, EC P-256 → `ecdsa-p256-sha256`. Any other key
93
+ * is `err`, naming what it is.
94
+ */
95
+ export function createExportSignerFromPem(privateKeyPem) {
96
+ let privateKey;
97
+ try {
98
+ privateKey = createPrivateKey({ key: privateKeyPem, format: 'pem' });
99
+ }
100
+ catch (cause) {
101
+ return malformedKey('the export signing key is not a readable PEM private key', cause);
102
+ }
103
+ if (privateKey.asymmetricKeyType === 'ed25519')
104
+ return createEd25519ExportSigner({ privateKeyPem });
105
+ if (privateKey.asymmetricKeyType === 'ec')
106
+ return createEcdsaP256ExportSigner({ privateKeyPem });
107
+ return malformedKey(`the export signing key is ${describeKeyType(privateKey)}: exports are signed with an Ed25519 or an EC P-256 key`);
108
+ }
109
+ /**
110
+ * An ECDSA signature from DER (`SEQUENCE { INTEGER r, INTEGER s }`, what
111
+ * Cloud KMS and AWS KMS answer) to IEEE P1363 `r‖s`, each half `size`
112
+ * bytes (32 for P-256): the form exports carry and Web Crypto verifies.
113
+ */
114
+ export function ecdsaDerToP1363(der, size = 32) {
115
+ const bad = () => malformedKey('the ECDSA signature is not a DER SEQUENCE of two INTEGERs');
116
+ let at = 0;
117
+ const readLength = () => {
118
+ const first = der[at++];
119
+ if (first === undefined)
120
+ return undefined;
121
+ if (first < 0x80)
122
+ return first;
123
+ const bytes = first & 0x7f;
124
+ if (bytes === 0 || bytes > 2)
125
+ return undefined;
126
+ let length = 0;
127
+ for (let i = 0; i < bytes; i += 1) {
128
+ const b = der[at++];
129
+ if (b === undefined)
130
+ return undefined;
131
+ length = length * 256 + b;
132
+ }
133
+ return length;
134
+ };
135
+ const readInteger = () => {
136
+ if (der[at++] !== 0x02)
137
+ return undefined;
138
+ const length = readLength();
139
+ if (length === undefined || at + length > der.length)
140
+ return undefined;
141
+ let value = der.subarray(at, at + length);
142
+ at += length;
143
+ while (value.length > size && value[0] === 0)
144
+ value = value.subarray(1);
145
+ if (value.length > size)
146
+ return undefined;
147
+ const out = new Uint8Array(size);
148
+ out.set(value, size - value.length);
149
+ return out;
150
+ };
151
+ if (der[at++] !== 0x30 || readLength() === undefined)
152
+ return bad();
153
+ const r = readInteger();
154
+ const s = readInteger();
155
+ if (r === undefined || s === undefined || at !== der.length)
156
+ return bad();
157
+ const out = new Uint8Array(size * 2);
158
+ out.set(r, 0);
159
+ out.set(s, size);
160
+ return ok(out);
161
+ }
162
+ /**
163
+ * An export signer over an Ed25519 private key held in memory: a PEM
164
+ * file's contents, a base64 env value decoded, a test's or a
165
+ * development server's generated key. `err` when the key isn't an
166
+ * Ed25519 PKCS#8 key.
167
+ */
168
+ export function createEd25519ExportSigner(input) {
169
+ const seed = 'privateKeyPem' in input ? parsePrivateKeyPem(input.privateKeyPem) : ok(input.privateKey);
170
+ if (seed.kind === 'err')
171
+ return seed;
172
+ const publicKey = publicKeyFromSeed(seed.value);
173
+ if (publicKey.kind === 'err')
174
+ return publicKey;
175
+ const key = exportSigningKey(publicKey.value);
176
+ const privateKey = seed.value;
177
+ return ok({
178
+ activeKey: () => key,
179
+ listKeys: () => [key],
180
+ async sign(bytes, options) {
181
+ if (options?.keyId !== undefined && options.keyId !== key.keyId) {
182
+ return { kind: 'err', error: keyNotFound(options.keyId, [key]) };
183
+ }
184
+ return signedWith(key, privateKey, bytes);
185
+ },
186
+ });
187
+ }
188
+ /**
189
+ * The `ExportSigningBinding` over a `SigningKeyBinding`'s Ed25519 keys,
190
+ * under the ids that binding gives them; the first is the active one.
191
+ * `undefined` when it holds no Ed25519 key (nothing to sign with).
192
+ */
193
+ export function exportSignerFromSigningKeyBinding(binding) {
194
+ const keys = binding
195
+ .listKeys()
196
+ .filter((d) => d.algorithm === 'ed25519')
197
+ .flatMap((d) => {
198
+ const pub = binding.getPublicKey(d.keyId);
199
+ return pub === null ? [] : [describeKey(d.keyId, pub)];
200
+ });
201
+ const [active] = keys;
202
+ if (active === undefined)
203
+ return undefined;
204
+ return {
205
+ activeKey: () => active,
206
+ listKeys: () => keys,
207
+ async sign(bytes, options) {
208
+ const key = options?.keyId === undefined ? active : keys.find((k) => k.keyId === options.keyId);
209
+ const privateKey = key === undefined ? null : binding.getPrivateKey(key.keyId);
210
+ if (key === undefined || privateKey === null) {
211
+ return { kind: 'err', error: keyNotFound(options?.keyId ?? active.keyId, keys) };
212
+ }
213
+ return signedWith(key, privateKey, bytes);
214
+ },
215
+ };
216
+ }
217
+ function signedWith(key, privateKey, bytes) {
218
+ const signed = signEd25519(privateKey, bytes);
219
+ if (signed.kind === 'err') {
220
+ return {
221
+ kind: 'err',
222
+ error: { code: 'export-signing-failed', message: signed.error.message, cause: signed.error },
223
+ };
224
+ }
225
+ return ok({ key, signature: signed.value });
226
+ }
227
+ function keyNotFound(keyId, keys) {
228
+ return {
229
+ code: 'signing-key-not-found',
230
+ message: `This deployment doesn't sign exports with key "${keyId}". It signs with ${keys.map((k) => `"${k.keyId}"`).join(', ')}; leave signingKeyId out to use the active one.`,
231
+ keyId,
232
+ };
233
+ }
234
+ function describeKey(keyId, publicKey) {
235
+ return {
236
+ keyId,
237
+ algorithm: 'ed25519',
238
+ publicKeyPem: serializePublicKeyPem(publicKey),
239
+ fingerprint: `sha256:${sha256(publicKey).toString('hex')}`,
240
+ };
241
+ }
242
+ function publicKeyFromSeed(seed) {
243
+ if (seed.length !== ED25519_RAW_KEY_BYTES) {
244
+ return malformedKey(`Ed25519 private key must be ${ED25519_RAW_KEY_BYTES} raw bytes, got ${seed.length}`);
245
+ }
246
+ try {
247
+ const priv = createPrivateKey({
248
+ key: Buffer.from(wrapPrivateKeyPkcs8(seed)),
249
+ format: 'der',
250
+ type: 'pkcs8',
251
+ });
252
+ const spki = createPublicKey(priv).export({ format: 'der', type: 'spki' });
253
+ return unwrapPublicKeySpki(new Uint8Array(spki));
254
+ }
255
+ catch (cause) {
256
+ return malformedKey('Ed25519 private key rejected by underlying primitive', cause);
257
+ }
258
+ }
259
+ /** A 65-byte uncompressed P-256 point as a PEM SPKI public key. */
260
+ function p256PublicKeyPem(point) {
261
+ const key = createPublicKey({
262
+ key: {
263
+ kty: 'EC',
264
+ crv: 'P-256',
265
+ x: Buffer.from(point.subarray(1, 33)).toString('base64url'),
266
+ y: Buffer.from(point.subarray(33, 65)).toString('base64url'),
267
+ },
268
+ format: 'jwk',
269
+ });
270
+ return key.export({ format: 'pem', type: 'spki' }).toString();
271
+ }
272
+ function describeKeyType(key) {
273
+ const type = key.asymmetricKeyType ?? 'an unknown kind of key';
274
+ const curve = key.asymmetricKeyDetails?.namedCurve;
275
+ return curve !== undefined ? `an ${type} key on ${curve}` : `an ${type} key`;
276
+ }
277
+ function sha256(bytes) {
278
+ return createHash('sha256').update(bytes).digest();
279
+ }
280
+ function ok(value) {
281
+ return { kind: 'ok', value };
282
+ }
283
+ //# sourceMappingURL=export-signing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export-signing.js","sourceRoot":"","sources":["../src/export-signing.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,8 @@ export { hmacSha256Sign, hmacSha256Verify } from './hmac.js';
4
4
  export { DEFAULT_WEBHOOK_TOLERANCE_SECONDS, generateWebhookSecret, isStrongWebhookSecret, signWebhook, verifyWebhook, WEBHOOK_HEADERS, WEBHOOK_SECRET_MIN_BYTES, WEBHOOK_SECRET_PREFIX, webhookHeaders, } from './webhook.js';
5
5
  export type { SignWebhookInput, VerifyWebhookFailure, VerifyWebhookInput, VerifyWebhookResult, WebhookRequestHeaders, } from './webhook.js';
6
6
  export { createInMemorySigningKeyBinding } from './binding.js';
7
+ export { EXPORT_SIGNING_ALGORITHMS, createEcdsaP256ExportSigner, createEd25519ExportSigner, createExportSignerFromPem, ecdsaDerToP1363, exportSignerFromSigningKeyBinding, exportSigningKey, } from './export-signing.js';
8
+ export type { ExportSignature, ExportSigningAlgorithm, ExportSigningBinding, ExportSigningError, ExportSigningKey, } from './export-signing.js';
7
9
  export type { SigningAlgorithm, SigningKeyBinding, SigningKeyDescriptor } from './binding.js';
8
10
  export type { CryptoError, MalformedKeyError, MalformedSignatureError, UnsupportedFormatError, } from './errors.js';
9
11
  export { ED25519_RAW_KEY_BYTES } from './encoding.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,WAAW,EACX,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7D,OAAO,EACL,iCAAiC,EACjC,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAC/D,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAE9F,YAAY,EACV,WAAW,EACX,iBAAiB,EACjB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,WAAW,EACX,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7D,OAAO,EACL,iCAAiC,EACjC,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,yBAAyB,EACzB,eAAe,EACf,iCAAiC,EACjC,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAE9F,YAAY,EACV,WAAW,EACX,iBAAiB,EACjB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -4,5 +4,6 @@ export { ED25519_SIGNATURE_BYTES, generateEd25519KeyPair, parsePrivateKeyBase64,
4
4
  export { hmacSha256Sign, hmacSha256Verify } from './hmac.js';
5
5
  export { DEFAULT_WEBHOOK_TOLERANCE_SECONDS, generateWebhookSecret, isStrongWebhookSecret, signWebhook, verifyWebhook, WEBHOOK_HEADERS, WEBHOOK_SECRET_MIN_BYTES, WEBHOOK_SECRET_PREFIX, webhookHeaders, } from './webhook.js';
6
6
  export { createInMemorySigningKeyBinding } from './binding.js';
7
+ export { EXPORT_SIGNING_ALGORITHMS, createEcdsaP256ExportSigner, createEd25519ExportSigner, createExportSignerFromPem, ecdsaDerToP1363, exportSignerFromSigningKeyBinding, exportSigningKey, } from './export-signing.js';
7
8
  export { ED25519_RAW_KEY_BYTES } from './encoding.js';
8
9
  //# 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":"AAAA,sCAAsC;AACtC,iCAAiC;AAEjC,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,WAAW,EACX,aAAa,GACd,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7D,OAAO,EACL,iCAAiC,EACjC,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,cAAc,GACf,MAAM,cAAc,CAAC;AAStB,OAAO,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAU/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAEjC,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,WAAW,EACX,aAAa,GACd,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7D,OAAO,EACL,iCAAiC,EACjC,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,cAAc,GACf,MAAM,cAAc,CAAC;AAStB,OAAO,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,yBAAyB,EACzB,eAAe,EACf,iCAAiC,EACjC,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAiB7B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kindgi/crypto",
3
- "version": "0.1.4",
3
+ "version": "0.1.5-rc.0",
4
4
  "description": "Kindgi™ cryptographic primitives. Ed25519 signatures for asymmetric use (signed audit bundles, provenance exports, deployment attestations) and HMAC-SHA256 for symmetric use (webhook payloads). Includes SigningKeyBinding interface + in-memory reference impl, PEM/base64 key format converters, and typed error interfaces. Pure Node built-ins, zero runtime deps. Deployments plug their own key store (KMS, Vault, env-var) via SigningKeyBinding.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -28,7 +28,7 @@
28
28
  "README.md"
29
29
  ],
30
30
  "dependencies": {
31
- "@kindgi/types": "0.1.4"
31
+ "@kindgi/types": "0.1.5-rc.0"
32
32
  },
33
33
  "engines": {
34
34
  "node": ">=22.12.0"
@@ -0,0 +1,364 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ /**
5
+ * `ExportSigningBinding` — the key a deployment signs its exports with:
6
+ * audit bundles, provenance exports and compliance evidence. Async, so
7
+ * a KMS that never hands out its private key (Cloud KMS
8
+ * `asymmetricSign`) can implement it as well as a key file can.
9
+ *
10
+ * One key signs at a time (`activeKey`); `listKeys` is every key a
11
+ * verifier should trust, active first. A key's id is derived from its
12
+ * public key (`exportSigningKey`), so the same key has the same id
13
+ * across restarts and deployments, with nothing to configure.
14
+ *
15
+ * Two algorithms, chosen per key (`ExportSigningKey.algorithm`):
16
+ * `ed25519`, the default for a key in memory or a file, and
17
+ * `ecdsa-p256-sha256`, for a KMS that has no Ed25519. An ECDSA signature
18
+ * travels as IEEE P1363 `r‖s` (64 bytes), the form Web Crypto verifies;
19
+ * a KMS that answers DER converts with `ecdsaDerToP1363`.
20
+ *
21
+ * Not the same seam as `SigningKeyBinding` (raw key bytes, read
22
+ * synchronously, for public run tokens on the request path).
23
+ * `exportSignerFromSigningKeyBinding` adapts one to the other.
24
+ */
25
+
26
+ import {
27
+ type KeyObject,
28
+ createHash,
29
+ createPrivateKey,
30
+ createPublicKey,
31
+ sign as nodeSign,
32
+ } from 'node:crypto';
33
+
34
+ import type { Result } from '@kindgi/types';
35
+
36
+ import type { SigningKeyBinding } from './binding.js';
37
+ import { parsePrivateKeyPem, serializePublicKeyPem, signEd25519 } from './ed25519.js';
38
+ import { ED25519_RAW_KEY_BYTES, unwrapPublicKeySpki, wrapPrivateKeyPkcs8 } from './encoding.js';
39
+ import type { CryptoError } from './errors.js';
40
+ import { malformedKey } from './errors.js';
41
+
42
+ /** The algorithms an export can be signed with. */
43
+ export const EXPORT_SIGNING_ALGORITHMS = ['ed25519', 'ecdsa-p256-sha256'] as const;
44
+ export type ExportSigningAlgorithm = (typeof EXPORT_SIGNING_ALGORITHMS)[number];
45
+
46
+ /** A key exports are signed with, as a verifier sees it. No private material. */
47
+ export interface ExportSigningKey {
48
+ /**
49
+ * `ex_` and 16 base64url characters of the SHA-256 of the raw public
50
+ * key: Ed25519's 32 bytes, P-256's 65-byte uncompressed point.
51
+ */
52
+ readonly keyId: string;
53
+ readonly algorithm: ExportSigningAlgorithm;
54
+ /** The public key, PEM SPKI. */
55
+ readonly publicKeyPem: string;
56
+ /** `sha256:` and the hex SHA-256 of the raw public key: to pin it, or compare by eye. */
57
+ readonly fingerprint: string;
58
+ }
59
+
60
+ export type ExportSigningError =
61
+ | {
62
+ /** `keyId` was asked for and isn't a key this binding signs with. */
63
+ readonly code: 'signing-key-not-found';
64
+ readonly message: string;
65
+ readonly keyId: string;
66
+ }
67
+ | {
68
+ /** The signer failed: a KMS that refused or didn't answer, a malformed key. */
69
+ readonly code: 'export-signing-failed';
70
+ readonly message: string;
71
+ readonly cause?: unknown;
72
+ };
73
+
74
+ export interface ExportSignature {
75
+ readonly key: ExportSigningKey;
76
+ readonly signature: Uint8Array;
77
+ }
78
+
79
+ export interface ExportSigningBinding {
80
+ /** The key `sign` uses when no `keyId` is asked for. */
81
+ activeKey(): ExportSigningKey;
82
+ /** Every key a verifier should trust, the active one first. */
83
+ listKeys(): readonly ExportSigningKey[];
84
+ /**
85
+ * Sign `bytes` with the active key, or with `keyId` when given. A
86
+ * `keyId` this binding can't sign with is `signing-key-not-found`.
87
+ */
88
+ sign(
89
+ bytes: Uint8Array,
90
+ options?: { readonly keyId?: string },
91
+ ): Promise<Result<ExportSignature, ExportSigningError>>;
92
+ }
93
+
94
+ /**
95
+ * The `ExportSigningKey` for a raw public key, under its derived id: a
96
+ * 32-byte Ed25519 key (the default), or a 65-byte uncompressed P-256 point
97
+ * for `ecdsa-p256-sha256`.
98
+ */
99
+ export function exportSigningKey(
100
+ publicKey: Uint8Array,
101
+ algorithm: ExportSigningAlgorithm = 'ed25519',
102
+ ): ExportSigningKey {
103
+ const keyId = `ex_${sha256(publicKey).toString('base64url').slice(0, 16)}`;
104
+ return algorithm === 'ed25519'
105
+ ? describeKey(keyId, publicKey)
106
+ : {
107
+ keyId,
108
+ algorithm,
109
+ publicKeyPem: p256PublicKeyPem(publicKey),
110
+ fingerprint: `sha256:${sha256(publicKey).toString('hex')}`,
111
+ };
112
+ }
113
+
114
+ /**
115
+ * An export signer over an EC P-256 private key held in memory (PKCS#8 or
116
+ * SEC1 PEM): `ecdsa-p256-sha256`, its signatures as P1363 `r‖s`. `err`
117
+ * when the key isn't a P-256 private key.
118
+ */
119
+ export function createEcdsaP256ExportSigner(input: {
120
+ readonly privateKeyPem: string;
121
+ }): Result<ExportSigningBinding, CryptoError> {
122
+ let privateKey: KeyObject;
123
+ try {
124
+ privateKey = createPrivateKey({ key: input.privateKeyPem, format: 'pem' });
125
+ } catch (cause) {
126
+ return malformedKey('the export signing key is not a readable PEM private key', cause);
127
+ }
128
+ const curve = privateKey.asymmetricKeyDetails?.namedCurve;
129
+ if (privateKey.asymmetricKeyType !== 'ec' || curve !== 'prime256v1') {
130
+ return malformedKey(
131
+ `the export signing key is ${describeKeyType(privateKey)}, not an EC P-256 key`,
132
+ );
133
+ }
134
+ const jwk = createPublicKey(privateKey).export({ format: 'jwk' });
135
+ const point = Buffer.concat([
136
+ Buffer.from([0x04]),
137
+ Buffer.from(jwk.x as string, 'base64url'),
138
+ Buffer.from(jwk.y as string, 'base64url'),
139
+ ]);
140
+ const key = exportSigningKey(new Uint8Array(point), 'ecdsa-p256-sha256');
141
+ return ok({
142
+ activeKey: () => key,
143
+ listKeys: () => [key],
144
+ async sign(bytes, options) {
145
+ if (options?.keyId !== undefined && options.keyId !== key.keyId) {
146
+ return { kind: 'err', error: keyNotFound(options.keyId, [key]) };
147
+ }
148
+ try {
149
+ const signature = nodeSign('sha256', bytes, { key: privateKey, dsaEncoding: 'ieee-p1363' });
150
+ return ok({ key, signature: new Uint8Array(signature) });
151
+ } catch (cause) {
152
+ return {
153
+ kind: 'err',
154
+ error: { code: 'export-signing-failed', message: 'ECDSA P-256 signing failed', cause },
155
+ };
156
+ }
157
+ },
158
+ });
159
+ }
160
+
161
+ /**
162
+ * The export signer for a PEM private key, its algorithm read from the
163
+ * key: Ed25519 → `ed25519`, EC P-256 → `ecdsa-p256-sha256`. Any other key
164
+ * is `err`, naming what it is.
165
+ */
166
+ export function createExportSignerFromPem(
167
+ privateKeyPem: string,
168
+ ): Result<ExportSigningBinding, CryptoError> {
169
+ let privateKey: KeyObject;
170
+ try {
171
+ privateKey = createPrivateKey({ key: privateKeyPem, format: 'pem' });
172
+ } catch (cause) {
173
+ return malformedKey('the export signing key is not a readable PEM private key', cause);
174
+ }
175
+ if (privateKey.asymmetricKeyType === 'ed25519')
176
+ return createEd25519ExportSigner({ privateKeyPem });
177
+ if (privateKey.asymmetricKeyType === 'ec') return createEcdsaP256ExportSigner({ privateKeyPem });
178
+ return malformedKey(
179
+ `the export signing key is ${describeKeyType(privateKey)}: exports are signed with an Ed25519 or an EC P-256 key`,
180
+ );
181
+ }
182
+
183
+ /**
184
+ * An ECDSA signature from DER (`SEQUENCE { INTEGER r, INTEGER s }`, what
185
+ * Cloud KMS and AWS KMS answer) to IEEE P1363 `r‖s`, each half `size`
186
+ * bytes (32 for P-256): the form exports carry and Web Crypto verifies.
187
+ */
188
+ export function ecdsaDerToP1363(der: Uint8Array, size = 32): Result<Uint8Array, CryptoError> {
189
+ const bad = () => malformedKey('the ECDSA signature is not a DER SEQUENCE of two INTEGERs');
190
+ let at = 0;
191
+ const readLength = (): number | undefined => {
192
+ const first = der[at++];
193
+ if (first === undefined) return undefined;
194
+ if (first < 0x80) return first;
195
+ const bytes = first & 0x7f;
196
+ if (bytes === 0 || bytes > 2) return undefined;
197
+ let length = 0;
198
+ for (let i = 0; i < bytes; i += 1) {
199
+ const b = der[at++];
200
+ if (b === undefined) return undefined;
201
+ length = length * 256 + b;
202
+ }
203
+ return length;
204
+ };
205
+ const readInteger = (): Uint8Array | undefined => {
206
+ if (der[at++] !== 0x02) return undefined;
207
+ const length = readLength();
208
+ if (length === undefined || at + length > der.length) return undefined;
209
+ let value = der.subarray(at, at + length);
210
+ at += length;
211
+ while (value.length > size && value[0] === 0) value = value.subarray(1);
212
+ if (value.length > size) return undefined;
213
+ const out = new Uint8Array(size);
214
+ out.set(value, size - value.length);
215
+ return out;
216
+ };
217
+ if (der[at++] !== 0x30 || readLength() === undefined) return bad();
218
+ const r = readInteger();
219
+ const s = readInteger();
220
+ if (r === undefined || s === undefined || at !== der.length) return bad();
221
+ const out = new Uint8Array(size * 2);
222
+ out.set(r, 0);
223
+ out.set(s, size);
224
+ return ok(out);
225
+ }
226
+
227
+ /**
228
+ * An export signer over an Ed25519 private key held in memory: a PEM
229
+ * file's contents, a base64 env value decoded, a test's or a
230
+ * development server's generated key. `err` when the key isn't an
231
+ * Ed25519 PKCS#8 key.
232
+ */
233
+ export function createEd25519ExportSigner(
234
+ input: { readonly privateKeyPem: string } | { readonly privateKey: Uint8Array },
235
+ ): Result<ExportSigningBinding, CryptoError> {
236
+ const seed =
237
+ 'privateKeyPem' in input ? parsePrivateKeyPem(input.privateKeyPem) : ok(input.privateKey);
238
+ if (seed.kind === 'err') return seed;
239
+ const publicKey = publicKeyFromSeed(seed.value);
240
+ if (publicKey.kind === 'err') return publicKey;
241
+ const key = exportSigningKey(publicKey.value);
242
+ const privateKey = seed.value;
243
+ return ok({
244
+ activeKey: () => key,
245
+ listKeys: () => [key],
246
+ async sign(bytes, options) {
247
+ if (options?.keyId !== undefined && options.keyId !== key.keyId) {
248
+ return { kind: 'err', error: keyNotFound(options.keyId, [key]) };
249
+ }
250
+ return signedWith(key, privateKey, bytes);
251
+ },
252
+ });
253
+ }
254
+
255
+ /**
256
+ * The `ExportSigningBinding` over a `SigningKeyBinding`'s Ed25519 keys,
257
+ * under the ids that binding gives them; the first is the active one.
258
+ * `undefined` when it holds no Ed25519 key (nothing to sign with).
259
+ */
260
+ export function exportSignerFromSigningKeyBinding(
261
+ binding: SigningKeyBinding,
262
+ ): ExportSigningBinding | undefined {
263
+ const keys = binding
264
+ .listKeys()
265
+ .filter((d) => d.algorithm === 'ed25519')
266
+ .flatMap((d) => {
267
+ const pub = binding.getPublicKey(d.keyId);
268
+ return pub === null ? [] : [describeKey(d.keyId as unknown as string, pub)];
269
+ });
270
+ const [active] = keys;
271
+ if (active === undefined) return undefined;
272
+ return {
273
+ activeKey: () => active,
274
+ listKeys: () => keys,
275
+ async sign(bytes, options) {
276
+ const key =
277
+ options?.keyId === undefined ? active : keys.find((k) => k.keyId === options.keyId);
278
+ const privateKey = key === undefined ? null : binding.getPrivateKey(key.keyId as never);
279
+ if (key === undefined || privateKey === null) {
280
+ return { kind: 'err', error: keyNotFound(options?.keyId ?? active.keyId, keys) };
281
+ }
282
+ return signedWith(key, privateKey, bytes);
283
+ },
284
+ };
285
+ }
286
+
287
+ function signedWith(
288
+ key: ExportSigningKey,
289
+ privateKey: Uint8Array,
290
+ bytes: Uint8Array,
291
+ ): Result<ExportSignature, ExportSigningError> {
292
+ const signed = signEd25519(privateKey, bytes);
293
+ if (signed.kind === 'err') {
294
+ return {
295
+ kind: 'err',
296
+ error: { code: 'export-signing-failed', message: signed.error.message, cause: signed.error },
297
+ };
298
+ }
299
+ return ok({ key, signature: signed.value });
300
+ }
301
+
302
+ function keyNotFound(keyId: string, keys: readonly ExportSigningKey[]): ExportSigningError {
303
+ return {
304
+ code: 'signing-key-not-found',
305
+ message: `This deployment doesn't sign exports with key "${keyId}". It signs with ${keys.map((k) => `"${k.keyId}"`).join(', ')}; leave signingKeyId out to use the active one.`,
306
+ keyId,
307
+ };
308
+ }
309
+
310
+ function describeKey(keyId: string, publicKey: Uint8Array): ExportSigningKey {
311
+ return {
312
+ keyId,
313
+ algorithm: 'ed25519',
314
+ publicKeyPem: serializePublicKeyPem(publicKey),
315
+ fingerprint: `sha256:${sha256(publicKey).toString('hex')}`,
316
+ };
317
+ }
318
+
319
+ function publicKeyFromSeed(seed: Uint8Array): Result<Uint8Array, CryptoError> {
320
+ if (seed.length !== ED25519_RAW_KEY_BYTES) {
321
+ return malformedKey(
322
+ `Ed25519 private key must be ${ED25519_RAW_KEY_BYTES} raw bytes, got ${seed.length}`,
323
+ );
324
+ }
325
+ try {
326
+ const priv = createPrivateKey({
327
+ key: Buffer.from(wrapPrivateKeyPkcs8(seed)),
328
+ format: 'der',
329
+ type: 'pkcs8',
330
+ });
331
+ const spki = createPublicKey(priv).export({ format: 'der', type: 'spki' });
332
+ return unwrapPublicKeySpki(new Uint8Array(spki));
333
+ } catch (cause) {
334
+ return malformedKey('Ed25519 private key rejected by underlying primitive', cause);
335
+ }
336
+ }
337
+
338
+ /** A 65-byte uncompressed P-256 point as a PEM SPKI public key. */
339
+ function p256PublicKeyPem(point: Uint8Array): string {
340
+ const key = createPublicKey({
341
+ key: {
342
+ kty: 'EC',
343
+ crv: 'P-256',
344
+ x: Buffer.from(point.subarray(1, 33)).toString('base64url'),
345
+ y: Buffer.from(point.subarray(33, 65)).toString('base64url'),
346
+ },
347
+ format: 'jwk',
348
+ });
349
+ return key.export({ format: 'pem', type: 'spki' }).toString();
350
+ }
351
+
352
+ function describeKeyType(key: KeyObject): string {
353
+ const type = key.asymmetricKeyType ?? 'an unknown kind of key';
354
+ const curve = key.asymmetricKeyDetails?.namedCurve;
355
+ return curve !== undefined ? `an ${type} key on ${curve}` : `an ${type} key`;
356
+ }
357
+
358
+ function sha256(bytes: Uint8Array): Buffer {
359
+ return createHash('sha256').update(bytes).digest();
360
+ }
361
+
362
+ function ok<T>(value: T): { readonly kind: 'ok'; readonly value: T } {
363
+ return { kind: 'ok', value };
364
+ }
package/src/index.ts CHANGED
@@ -39,6 +39,23 @@ export type {
39
39
  } from './webhook.js';
40
40
 
41
41
  export { createInMemorySigningKeyBinding } from './binding.js';
42
+
43
+ export {
44
+ EXPORT_SIGNING_ALGORITHMS,
45
+ createEcdsaP256ExportSigner,
46
+ createEd25519ExportSigner,
47
+ createExportSignerFromPem,
48
+ ecdsaDerToP1363,
49
+ exportSignerFromSigningKeyBinding,
50
+ exportSigningKey,
51
+ } from './export-signing.js';
52
+ export type {
53
+ ExportSignature,
54
+ ExportSigningAlgorithm,
55
+ ExportSigningBinding,
56
+ ExportSigningError,
57
+ ExportSigningKey,
58
+ } from './export-signing.js';
42
59
  export type { SigningAlgorithm, SigningKeyBinding, SigningKeyDescriptor } from './binding.js';
43
60
 
44
61
  export type {