@kindgi/crypto 0.1.5 → 0.1.6-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 +0 -1
- package/dist/export-signing.d.ts +0 -7
- package/dist/export-signing.d.ts.map +1 -1
- package/dist/export-signing.js +0 -30
- package/dist/export-signing.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/retired-export-keys.d.ts +19 -0
- package/dist/retired-export-keys.d.ts.map +1 -0
- package/dist/retired-export-keys.js +99 -0
- package/dist/retired-export-keys.js.map +1 -0
- package/package.json +2 -2
- package/src/export-signing.ts +0 -35
- package/src/index.ts +1 -1
- package/src/retired-export-keys.ts +122 -0
package/README.md
CHANGED
|
@@ -123,7 +123,6 @@ const signed = await signer.sign(bytes); // the active key, or { keyId }
|
|
|
123
123
|
|
|
124
124
|
- **Key ids are derived from the public key** (`exportSigningKey`), so the same key keeps its id.
|
|
125
125
|
- **`listKeys()`** is every key a verifier should trust, active first.
|
|
126
|
-
- **`exportSignerFromSigningKeyBinding`** adapts a `SigningKeyBinding`'s Ed25519 keys.
|
|
127
126
|
- **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
127
|
- **Test vectors** for both are in `@kindgi/specs` (`test-vectors/signed-export/`).
|
|
129
128
|
|
package/dist/export-signing.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Result } from '@kindgi/types';
|
|
2
|
-
import type { SigningKeyBinding } from './binding.js';
|
|
3
2
|
import type { CryptoError } from './errors.js';
|
|
4
3
|
/** The algorithms an export can be signed with. */
|
|
5
4
|
export declare const EXPORT_SIGNING_ALGORITHMS: readonly ["ed25519", "ecdsa-p256-sha256"];
|
|
@@ -82,10 +81,4 @@ export declare function createEd25519ExportSigner(input: {
|
|
|
82
81
|
} | {
|
|
83
82
|
readonly privateKey: Uint8Array;
|
|
84
83
|
}): 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
84
|
//# sourceMappingURL=export-signing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export-signing.d.ts","sourceRoot":"","sources":["../src/export-signing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"export-signing.d.ts","sourceRoot":"","sources":["../src/export-signing.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAG5C,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"}
|
package/dist/export-signing.js
CHANGED
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Not the same seam as `SigningKeyBinding` (raw key bytes, read
|
|
21
21
|
* synchronously, for public run tokens on the request path).
|
|
22
|
-
* `exportSignerFromSigningKeyBinding` adapts one to the other.
|
|
23
22
|
*/
|
|
24
23
|
import { createHash, createPrivateKey, createPublicKey, sign as nodeSign, } from 'node:crypto';
|
|
25
24
|
import { parsePrivateKeyPem, serializePublicKeyPem, signEd25519 } from './ed25519.js';
|
|
@@ -185,35 +184,6 @@ export function createEd25519ExportSigner(input) {
|
|
|
185
184
|
},
|
|
186
185
|
});
|
|
187
186
|
}
|
|
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
187
|
function signedWith(key, privateKey, bytes) {
|
|
218
188
|
const signed = signEd25519(privateKey, bytes);
|
|
219
189
|
if (signed.kind === 'err') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export-signing.js","sourceRoot":"","sources":["../src/export-signing.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAEjC
|
|
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,7 +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,
|
|
7
|
+
export { EXPORT_SIGNING_ALGORITHMS, createEcdsaP256ExportSigner, createEd25519ExportSigner, createExportSignerFromPem, ecdsaDerToP1363, exportSigningKey, } from './export-signing.js';
|
|
8
|
+
export { parseRetiredExportKeys, withRetiredExportKeys } from './retired-export-keys.js';
|
|
8
9
|
export type { ExportSignature, ExportSigningAlgorithm, ExportSigningBinding, ExportSigningError, ExportSigningKey, } from './export-signing.js';
|
|
9
10
|
export type { SigningAlgorithm, SigningKeyBinding, SigningKeyDescriptor } from './binding.js';
|
|
10
11
|
export type { CryptoError, MalformedKeyError, MalformedSignatureError, UnsupportedFormatError, } from './errors.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;AAE/D,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,yBAAyB,EACzB,eAAe,EACf,
|
|
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,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACzF,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,6 +4,7 @@ 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,
|
|
7
|
+
export { EXPORT_SIGNING_ALGORITHMS, createEcdsaP256ExportSigner, createEd25519ExportSigner, createExportSignerFromPem, ecdsaDerToP1363, exportSigningKey, } from './export-signing.js';
|
|
8
|
+
export { parseRetiredExportKeys, withRetiredExportKeys } from './retired-export-keys.js';
|
|
8
9
|
export { ED25519_RAW_KEY_BYTES } from './encoding.js';
|
|
9
10
|
//# 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;AAE/D,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,yBAAyB,EACzB,eAAe,EACf,
|
|
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,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAiBzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Result } from '@kindgi/types';
|
|
2
|
+
import type { CryptoError } from './errors.js';
|
|
3
|
+
import { type ExportSigningBinding, type ExportSigningKey } from './export-signing.js';
|
|
4
|
+
/**
|
|
5
|
+
* The export keys in a PEM bundle: one or more `PUBLIC KEY` blocks (SPKI),
|
|
6
|
+
* Ed25519 or EC P-256, in order, under the ids `exportSigningKey` derives.
|
|
7
|
+
* `err` names the block (by its position) that's a private key, another
|
|
8
|
+
* kind of block, unreadable, or a key of another kind; and a bundle with
|
|
9
|
+
* no block at all.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseRetiredExportKeys(pemBundle: string): Result<readonly ExportSigningKey[], CryptoError>;
|
|
12
|
+
/**
|
|
13
|
+
* `binding` with `retired` listed after its own keys (a key it already
|
|
14
|
+
* lists, or one given twice, once). The active key and `sign` stay
|
|
15
|
+
* `binding`'s, so signing with a retired key's id is
|
|
16
|
+
* `signing-key-not-found`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function withRetiredExportKeys(binding: ExportSigningBinding, retired: readonly ExportSigningKey[]): ExportSigningBinding;
|
|
19
|
+
//# sourceMappingURL=retired-export-keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retired-export-keys.d.ts","sourceRoot":"","sources":["../src/retired-export-keys.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAG5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EAEtB,MAAM,qBAAqB,CAAC;AAI7B;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,SAAS,gBAAgB,EAAE,EAAE,WAAW,CAAC,CA+BlD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,SAAS,gBAAgB,EAAE,GACnC,oBAAoB,CAiBtB"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
/**
|
|
4
|
+
* Retired export keys: public keys a deployment no longer signs with,
|
|
5
|
+
* listed after its own. A verifier that pins `GET /v1/export-signing-keys`
|
|
6
|
+
* (`kindgi exports verify --from-runtime`) then still trusts an export
|
|
7
|
+
* signed before a key rotation. A retired key never signs: only public
|
|
8
|
+
* keys go in, and `sign` stays the binding's own.
|
|
9
|
+
*/
|
|
10
|
+
import { createPublicKey } from 'node:crypto';
|
|
11
|
+
import { unwrapPublicKeySpki } from './encoding.js';
|
|
12
|
+
import { malformedKey } from './errors.js';
|
|
13
|
+
import { exportSigningKey, } from './export-signing.js';
|
|
14
|
+
const PEM_BLOCK = /-----BEGIN ([A-Z0-9 ]+)-----[\s\S]*?-----END \1-----/g;
|
|
15
|
+
/**
|
|
16
|
+
* The export keys in a PEM bundle: one or more `PUBLIC KEY` blocks (SPKI),
|
|
17
|
+
* Ed25519 or EC P-256, in order, under the ids `exportSigningKey` derives.
|
|
18
|
+
* `err` names the block (by its position) that's a private key, another
|
|
19
|
+
* kind of block, unreadable, or a key of another kind; and a bundle with
|
|
20
|
+
* no block at all.
|
|
21
|
+
*/
|
|
22
|
+
export function parseRetiredExportKeys(pemBundle) {
|
|
23
|
+
const blocks = [...pemBundle.matchAll(PEM_BLOCK)];
|
|
24
|
+
if (blocks.length === 0)
|
|
25
|
+
return malformedKey('it holds no PEM block (-----BEGIN PUBLIC KEY-----)');
|
|
26
|
+
const keys = [];
|
|
27
|
+
for (const [i, block] of blocks.entries()) {
|
|
28
|
+
const where = `block ${i + 1} of ${blocks.length}`;
|
|
29
|
+
const label = block[1] ?? '';
|
|
30
|
+
if (label.includes('PRIVATE')) {
|
|
31
|
+
return malformedKey(`${where} is a private key (${label}): retired keys are public keys only`);
|
|
32
|
+
}
|
|
33
|
+
if (label !== 'PUBLIC KEY') {
|
|
34
|
+
return malformedKey(`${where} is a ${label}, not a PUBLIC KEY`);
|
|
35
|
+
}
|
|
36
|
+
let key;
|
|
37
|
+
try {
|
|
38
|
+
key = createPublicKey({ key: block[0], format: 'pem' });
|
|
39
|
+
}
|
|
40
|
+
catch (cause) {
|
|
41
|
+
return malformedKey(`${where} is not a readable PEM public key`, cause);
|
|
42
|
+
}
|
|
43
|
+
const described = describePublicKey(key);
|
|
44
|
+
if (described.kind === 'err') {
|
|
45
|
+
return malformedKey(`${where} is ${described.error}: exports are signed with Ed25519 or EC P-256 keys`);
|
|
46
|
+
}
|
|
47
|
+
keys.push(described.value);
|
|
48
|
+
}
|
|
49
|
+
return { kind: 'ok', value: keys };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* `binding` with `retired` listed after its own keys (a key it already
|
|
53
|
+
* lists, or one given twice, once). The active key and `sign` stay
|
|
54
|
+
* `binding`'s, so signing with a retired key's id is
|
|
55
|
+
* `signing-key-not-found`.
|
|
56
|
+
*/
|
|
57
|
+
export function withRetiredExportKeys(binding, retired) {
|
|
58
|
+
const listKeys = () => {
|
|
59
|
+
const own = binding.listKeys();
|
|
60
|
+
const seen = new Set(own.map((k) => k.keyId));
|
|
61
|
+
const extra = [];
|
|
62
|
+
for (const key of retired) {
|
|
63
|
+
if (seen.has(key.keyId))
|
|
64
|
+
continue;
|
|
65
|
+
seen.add(key.keyId);
|
|
66
|
+
extra.push(key);
|
|
67
|
+
}
|
|
68
|
+
return [...own, ...extra];
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
activeKey: () => binding.activeKey(),
|
|
72
|
+
listKeys,
|
|
73
|
+
sign: (bytes, options) => binding.sign(bytes, options),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function describePublicKey(key) {
|
|
77
|
+
if (key.asymmetricKeyType === 'ed25519') {
|
|
78
|
+
const raw = unwrapPublicKeySpki(new Uint8Array(key.export({ format: 'der', type: 'spki' })));
|
|
79
|
+
if (raw.kind === 'err')
|
|
80
|
+
return { kind: 'err', error: 'an Ed25519 key that could not be read' };
|
|
81
|
+
return { kind: 'ok', value: exportSigningKey(raw.value, 'ed25519') };
|
|
82
|
+
}
|
|
83
|
+
if (key.asymmetricKeyType === 'ec' && key.asymmetricKeyDetails?.namedCurve === 'prime256v1') {
|
|
84
|
+
const jwk = key.export({ format: 'jwk' });
|
|
85
|
+
const point = Buffer.concat([
|
|
86
|
+
Buffer.from([0x04]),
|
|
87
|
+
Buffer.from(String(jwk.x), 'base64url'),
|
|
88
|
+
Buffer.from(String(jwk.y), 'base64url'),
|
|
89
|
+
]);
|
|
90
|
+
return { kind: 'ok', value: exportSigningKey(new Uint8Array(point), 'ecdsa-p256-sha256') };
|
|
91
|
+
}
|
|
92
|
+
const type = key.asymmetricKeyType ?? 'an unknown kind of key';
|
|
93
|
+
const curve = key.asymmetricKeyDetails?.namedCurve;
|
|
94
|
+
return {
|
|
95
|
+
kind: 'err',
|
|
96
|
+
error: curve !== undefined ? `an ${type} key on ${curve}` : `an ${type} key`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=retired-export-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retired-export-keys.js","sourceRoot":"","sources":["../src/retired-export-keys.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kindgi/crypto",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6-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.
|
|
31
|
+
"@kindgi/types": "0.1.6-rc.0"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
|
34
34
|
"node": ">=22.12.0"
|
package/src/export-signing.ts
CHANGED
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
*
|
|
21
21
|
* Not the same seam as `SigningKeyBinding` (raw key bytes, read
|
|
22
22
|
* synchronously, for public run tokens on the request path).
|
|
23
|
-
* `exportSignerFromSigningKeyBinding` adapts one to the other.
|
|
24
23
|
*/
|
|
25
24
|
|
|
26
25
|
import {
|
|
@@ -32,8 +31,6 @@ import {
|
|
|
32
31
|
} from 'node:crypto';
|
|
33
32
|
|
|
34
33
|
import type { Result } from '@kindgi/types';
|
|
35
|
-
|
|
36
|
-
import type { SigningKeyBinding } from './binding.js';
|
|
37
34
|
import { parsePrivateKeyPem, serializePublicKeyPem, signEd25519 } from './ed25519.js';
|
|
38
35
|
import { ED25519_RAW_KEY_BYTES, unwrapPublicKeySpki, wrapPrivateKeyPkcs8 } from './encoding.js';
|
|
39
36
|
import type { CryptoError } from './errors.js';
|
|
@@ -252,38 +249,6 @@ export function createEd25519ExportSigner(
|
|
|
252
249
|
});
|
|
253
250
|
}
|
|
254
251
|
|
|
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
252
|
function signedWith(
|
|
288
253
|
key: ExportSigningKey,
|
|
289
254
|
privateKey: Uint8Array,
|
package/src/index.ts
CHANGED
|
@@ -46,9 +46,9 @@ export {
|
|
|
46
46
|
createEd25519ExportSigner,
|
|
47
47
|
createExportSignerFromPem,
|
|
48
48
|
ecdsaDerToP1363,
|
|
49
|
-
exportSignerFromSigningKeyBinding,
|
|
50
49
|
exportSigningKey,
|
|
51
50
|
} from './export-signing.js';
|
|
51
|
+
export { parseRetiredExportKeys, withRetiredExportKeys } from './retired-export-keys.js';
|
|
52
52
|
export type {
|
|
53
53
|
ExportSignature,
|
|
54
54
|
ExportSigningAlgorithm,
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Retired export keys: public keys a deployment no longer signs with,
|
|
6
|
+
* listed after its own. A verifier that pins `GET /v1/export-signing-keys`
|
|
7
|
+
* (`kindgi exports verify --from-runtime`) then still trusts an export
|
|
8
|
+
* signed before a key rotation. A retired key never signs: only public
|
|
9
|
+
* keys go in, and `sign` stays the binding's own.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { type KeyObject, createPublicKey } from 'node:crypto';
|
|
13
|
+
|
|
14
|
+
import type { Result } from '@kindgi/types';
|
|
15
|
+
|
|
16
|
+
import { unwrapPublicKeySpki } from './encoding.js';
|
|
17
|
+
import type { CryptoError } from './errors.js';
|
|
18
|
+
import { malformedKey } from './errors.js';
|
|
19
|
+
import {
|
|
20
|
+
type ExportSigningBinding,
|
|
21
|
+
type ExportSigningKey,
|
|
22
|
+
exportSigningKey,
|
|
23
|
+
} from './export-signing.js';
|
|
24
|
+
|
|
25
|
+
const PEM_BLOCK = /-----BEGIN ([A-Z0-9 ]+)-----[\s\S]*?-----END \1-----/g;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The export keys in a PEM bundle: one or more `PUBLIC KEY` blocks (SPKI),
|
|
29
|
+
* Ed25519 or EC P-256, in order, under the ids `exportSigningKey` derives.
|
|
30
|
+
* `err` names the block (by its position) that's a private key, another
|
|
31
|
+
* kind of block, unreadable, or a key of another kind; and a bundle with
|
|
32
|
+
* no block at all.
|
|
33
|
+
*/
|
|
34
|
+
export function parseRetiredExportKeys(
|
|
35
|
+
pemBundle: string,
|
|
36
|
+
): Result<readonly ExportSigningKey[], CryptoError> {
|
|
37
|
+
const blocks = [...pemBundle.matchAll(PEM_BLOCK)];
|
|
38
|
+
if (blocks.length === 0)
|
|
39
|
+
return malformedKey('it holds no PEM block (-----BEGIN PUBLIC KEY-----)');
|
|
40
|
+
const keys: ExportSigningKey[] = [];
|
|
41
|
+
for (const [i, block] of blocks.entries()) {
|
|
42
|
+
const where = `block ${i + 1} of ${blocks.length}`;
|
|
43
|
+
const label = block[1] ?? '';
|
|
44
|
+
if (label.includes('PRIVATE')) {
|
|
45
|
+
return malformedKey(
|
|
46
|
+
`${where} is a private key (${label}): retired keys are public keys only`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (label !== 'PUBLIC KEY') {
|
|
50
|
+
return malformedKey(`${where} is a ${label}, not a PUBLIC KEY`);
|
|
51
|
+
}
|
|
52
|
+
let key: KeyObject;
|
|
53
|
+
try {
|
|
54
|
+
key = createPublicKey({ key: block[0], format: 'pem' });
|
|
55
|
+
} catch (cause) {
|
|
56
|
+
return malformedKey(`${where} is not a readable PEM public key`, cause);
|
|
57
|
+
}
|
|
58
|
+
const described = describePublicKey(key);
|
|
59
|
+
if (described.kind === 'err') {
|
|
60
|
+
return malformedKey(
|
|
61
|
+
`${where} is ${described.error}: exports are signed with Ed25519 or EC P-256 keys`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
keys.push(described.value);
|
|
65
|
+
}
|
|
66
|
+
return { kind: 'ok', value: keys };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* `binding` with `retired` listed after its own keys (a key it already
|
|
71
|
+
* lists, or one given twice, once). The active key and `sign` stay
|
|
72
|
+
* `binding`'s, so signing with a retired key's id is
|
|
73
|
+
* `signing-key-not-found`.
|
|
74
|
+
*/
|
|
75
|
+
export function withRetiredExportKeys(
|
|
76
|
+
binding: ExportSigningBinding,
|
|
77
|
+
retired: readonly ExportSigningKey[],
|
|
78
|
+
): ExportSigningBinding {
|
|
79
|
+
const listKeys = (): readonly ExportSigningKey[] => {
|
|
80
|
+
const own = binding.listKeys();
|
|
81
|
+
const seen = new Set(own.map((k) => k.keyId));
|
|
82
|
+
const extra: ExportSigningKey[] = [];
|
|
83
|
+
for (const key of retired) {
|
|
84
|
+
if (seen.has(key.keyId)) continue;
|
|
85
|
+
seen.add(key.keyId);
|
|
86
|
+
extra.push(key);
|
|
87
|
+
}
|
|
88
|
+
return [...own, ...extra];
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
activeKey: () => binding.activeKey(),
|
|
92
|
+
listKeys,
|
|
93
|
+
sign: (bytes, options) => binding.sign(bytes, options),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function describePublicKey(
|
|
98
|
+
key: KeyObject,
|
|
99
|
+
):
|
|
100
|
+
| { readonly kind: 'ok'; readonly value: ExportSigningKey }
|
|
101
|
+
| { readonly kind: 'err'; readonly error: string } {
|
|
102
|
+
if (key.asymmetricKeyType === 'ed25519') {
|
|
103
|
+
const raw = unwrapPublicKeySpki(new Uint8Array(key.export({ format: 'der', type: 'spki' })));
|
|
104
|
+
if (raw.kind === 'err') return { kind: 'err', error: 'an Ed25519 key that could not be read' };
|
|
105
|
+
return { kind: 'ok', value: exportSigningKey(raw.value, 'ed25519') };
|
|
106
|
+
}
|
|
107
|
+
if (key.asymmetricKeyType === 'ec' && key.asymmetricKeyDetails?.namedCurve === 'prime256v1') {
|
|
108
|
+
const jwk = key.export({ format: 'jwk' });
|
|
109
|
+
const point = Buffer.concat([
|
|
110
|
+
Buffer.from([0x04]),
|
|
111
|
+
Buffer.from(String(jwk.x), 'base64url'),
|
|
112
|
+
Buffer.from(String(jwk.y), 'base64url'),
|
|
113
|
+
]);
|
|
114
|
+
return { kind: 'ok', value: exportSigningKey(new Uint8Array(point), 'ecdsa-p256-sha256') };
|
|
115
|
+
}
|
|
116
|
+
const type = key.asymmetricKeyType ?? 'an unknown kind of key';
|
|
117
|
+
const curve = key.asymmetricKeyDetails?.namedCurve;
|
|
118
|
+
return {
|
|
119
|
+
kind: 'err',
|
|
120
|
+
error: curve !== undefined ? `an ${type} key on ${curve}` : `an ${type} key`,
|
|
121
|
+
};
|
|
122
|
+
}
|