raz-encrypt 1.1.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +442 -212
- package/dist/canonicalJson.d.ts +15 -0
- package/dist/canonicalJson.js +73 -13
- package/dist/canonicalJson.js.map +1 -1
- package/dist/core.d.ts +4 -2
- package/dist/core.js +3 -1
- package/dist/core.js.map +1 -1
- package/dist/crypto/classical.d.ts +2 -0
- package/dist/crypto/classical.js +4 -0
- package/dist/crypto/classical.js.map +1 -1
- package/dist/crypto/confirmedSession.d.ts +47 -0
- package/dist/crypto/confirmedSession.js +84 -0
- package/dist/crypto/confirmedSession.js.map +1 -0
- package/dist/crypto/frameCipher.js +8 -2
- package/dist/crypto/frameCipher.js.map +1 -1
- package/dist/crypto/frameSession.d.ts +40 -0
- package/dist/crypto/frameSession.js +96 -0
- package/dist/crypto/frameSession.js.map +1 -0
- package/dist/crypto/hybrid.d.ts +95 -8
- package/dist/crypto/hybrid.js +374 -49
- package/dist/crypto/hybrid.js.map +1 -1
- package/dist/crypto/nodeHybrid.d.ts +6 -4
- package/dist/crypto/nodeHybrid.js +3 -2
- package/dist/crypto/nodeHybrid.js.map +1 -1
- package/dist/crypto/pqc.d.ts +7 -2
- package/dist/crypto/pqc.js +13 -4
- package/dist/crypto/pqc.js.map +1 -1
- package/dist/crypto/ratchet.js +5 -0
- package/dist/crypto/ratchet.js.map +1 -1
- package/dist/fileCrypto.d.ts +44 -2
- package/dist/fileCrypto.js +197 -28
- package/dist/fileCrypto.js.map +1 -1
- package/dist/identity.d.ts +69 -4
- package/dist/identity.js +111 -12
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/passphrase.d.ts +27 -0
- package/dist/passphrase.js +69 -0
- package/dist/passphrase.js.map +1 -0
- package/dist/store.d.ts +42 -5
- package/dist/store.js +254 -22
- package/dist/store.js.map +1 -1
- package/dist/trust.d.ts +143 -2
- package/dist/trust.js +376 -27
- package/dist/trust.js.map +1 -1
- package/package.json +62 -48
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* resolvable by a browser bundler with no node:fs in its module graph.
|
|
11
11
|
*/
|
|
12
12
|
import type { Identity, PublicCard } from "../identity.js";
|
|
13
|
-
import { type Certificate, type EncryptedBox, type SignOptions, type VerifyResult } from "./hybrid.js";
|
|
14
|
-
export { UntrustedPartyError } from "./hybrid.js";
|
|
15
|
-
export type { Certificate, EncryptedBox, SignOptions, VerifyFailureReason, VerifyResult } from "./hybrid.js";
|
|
13
|
+
import { type DecryptDeps, type Certificate, type EncryptedBox, type SignOptions, type VerifyResult } from "./hybrid.js";
|
|
14
|
+
export { DecryptionError, ReplayedBoxError, UntrustedPartyError } from "./hybrid.js";
|
|
15
|
+
export type { BoxReplayInfo, Certificate, EncryptedBox, SignOptions, VerifyFailureReason, VerifyResult } from "./hybrid.js";
|
|
16
16
|
/**
|
|
17
17
|
* Signs `document` with both the classical (ECDSA-P384) and post-quantum
|
|
18
18
|
* (ML-DSA-87) keys. Permanent by default; pass `options.expiresAt` to make
|
|
@@ -39,4 +39,6 @@ export declare function encryptFor(identity: Identity, counterpart: PublicCard,
|
|
|
39
39
|
* in `identity`'s local trust list — this stops a caller from accidentally
|
|
40
40
|
* decrypting "as if" a message came from someone they never actually trusted.
|
|
41
41
|
*/
|
|
42
|
-
export declare function decryptFrom<T = unknown>(identity: Identity, counterpart: PublicCard, box: EncryptedBox
|
|
42
|
+
export declare function decryptFrom<T = unknown>(identity: Identity, counterpart: PublicCard, box: EncryptedBox, options?: {
|
|
43
|
+
isReplay?: DecryptDeps["isReplay"];
|
|
44
|
+
}): Promise<T>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { appendLogEntry } from "../store.js";
|
|
2
2
|
import { getTrustRecordsByAlias, getTrustedPartiesByAlias } from "../trust.js";
|
|
3
3
|
import { decryptFromCore, encryptForCore, signCore, verifyCore, } from "./hybrid.js";
|
|
4
|
-
export { UntrustedPartyError } from "./hybrid.js";
|
|
4
|
+
export { DecryptionError, ReplayedBoxError, UntrustedPartyError } from "./hybrid.js";
|
|
5
5
|
async function trustStatusForSigner(identity, signer) {
|
|
6
6
|
const allRecords = await getTrustRecordsByAlias(identity, signer.alias);
|
|
7
7
|
const matchingRecord = allRecords.find((r) => r.card.classicalPublicKey === signer.classicalPublicKey && r.card.pqcSignPublicKey === signer.pqcSignPublicKey);
|
|
@@ -56,8 +56,9 @@ export async function encryptFor(identity, counterpart, data) {
|
|
|
56
56
|
* in `identity`'s local trust list — this stops a caller from accidentally
|
|
57
57
|
* decrypting "as if" a message came from someone they never actually trusted.
|
|
58
58
|
*/
|
|
59
|
-
export async function decryptFrom(identity, counterpart, box) {
|
|
59
|
+
export async function decryptFrom(identity, counterpart, box, options = {}) {
|
|
60
60
|
return decryptFromCore(identity, counterpart, box, {
|
|
61
|
+
isReplay: options.isReplay,
|
|
61
62
|
checkTrust: (card) => trustStatusForCard(identity, card),
|
|
62
63
|
onLog: (entry) => appendLogEntry(identity, entry),
|
|
63
64
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeHybrid.js","sourceRoot":"","sources":["../../src/crypto/nodeHybrid.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EACL,eAAe,
|
|
1
|
+
{"version":3,"file":"nodeHybrid.js","sourceRoot":"","sources":["../../src/crypto/nodeHybrid.ts"],"names":[],"mappings":"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"}
|
package/dist/crypto/pqc.d.ts
CHANGED
|
@@ -8,8 +8,9 @@ export interface PqcKemKeypair {
|
|
|
8
8
|
}
|
|
9
9
|
/** ML-DSA-87 (FIPS 204, category-5 parameter set) — the post-quantum signing layer. */
|
|
10
10
|
export declare function generatePqcSignKeypair(): PqcSignKeypair;
|
|
11
|
-
|
|
12
|
-
export declare function
|
|
11
|
+
/** `context` provides domain separation (FIPS 204 §5.4): omit it for document signing (unchanged), pass it for the session handshake so a handshake signature can't be reinterpreted in a different context. */
|
|
12
|
+
export declare function pqcSign(message: Uint8Array, secretKey: Uint8Array, context?: Uint8Array): Uint8Array;
|
|
13
|
+
export declare function pqcVerify(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array, context?: Uint8Array): boolean;
|
|
13
14
|
/** ML-KEM-1024 (FIPS 203, category-5 parameter set) — the post-quantum key-exchange layer. */
|
|
14
15
|
export declare function generatePqcKemKeypair(): PqcKemKeypair;
|
|
15
16
|
export declare function pqcEncapsulate(theirPublicKey: Uint8Array): {
|
|
@@ -17,3 +18,7 @@ export declare function pqcEncapsulate(theirPublicKey: Uint8Array): {
|
|
|
17
18
|
sharedSecret: Uint8Array;
|
|
18
19
|
};
|
|
19
20
|
export declare function pqcDecapsulate(cipherText: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
21
|
+
/** True iff `keypair`'s two fields are the exact byte lengths ml_dsa87's own keygen produces — a cheap sanity check for key material sourced from somewhere this library doesn't control (e.g. a caller-supplied KeyStore, issue #38). */
|
|
22
|
+
export declare function isValidPqcSignKeypairShape(keypair: PqcSignKeypair): boolean;
|
|
23
|
+
/** Same as isValidPqcSignKeypairShape, for ML-KEM-1024. */
|
|
24
|
+
export declare function isValidPqcKemKeypairShape(keypair: PqcKemKeypair): boolean;
|
package/dist/crypto/pqc.js
CHANGED
|
@@ -4,12 +4,13 @@ import { ml_kem1024 } from "@noble/post-quantum/ml-kem.js";
|
|
|
4
4
|
export function generatePqcSignKeypair() {
|
|
5
5
|
return ml_dsa87.keygen();
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/** `context` provides domain separation (FIPS 204 §5.4): omit it for document signing (unchanged), pass it for the session handshake so a handshake signature can't be reinterpreted in a different context. */
|
|
8
|
+
export function pqcSign(message, secretKey, context) {
|
|
9
|
+
return ml_dsa87.sign(message, secretKey, context ? { context } : undefined);
|
|
9
10
|
}
|
|
10
|
-
export function pqcVerify(signature, message, publicKey) {
|
|
11
|
+
export function pqcVerify(signature, message, publicKey, context) {
|
|
11
12
|
try {
|
|
12
|
-
return ml_dsa87.verify(signature, message, publicKey);
|
|
13
|
+
return ml_dsa87.verify(signature, message, publicKey, context ? { context } : undefined);
|
|
13
14
|
}
|
|
14
15
|
catch {
|
|
15
16
|
return false;
|
|
@@ -25,4 +26,12 @@ export function pqcEncapsulate(theirPublicKey) {
|
|
|
25
26
|
export function pqcDecapsulate(cipherText, secretKey) {
|
|
26
27
|
return ml_kem1024.decapsulate(cipherText, secretKey);
|
|
27
28
|
}
|
|
29
|
+
/** True iff `keypair`'s two fields are the exact byte lengths ml_dsa87's own keygen produces — a cheap sanity check for key material sourced from somewhere this library doesn't control (e.g. a caller-supplied KeyStore, issue #38). */
|
|
30
|
+
export function isValidPqcSignKeypairShape(keypair) {
|
|
31
|
+
return keypair.secretKey.length === ml_dsa87.lengths.secretKey && keypair.publicKey.length === ml_dsa87.lengths.publicKey;
|
|
32
|
+
}
|
|
33
|
+
/** Same as isValidPqcSignKeypairShape, for ML-KEM-1024. */
|
|
34
|
+
export function isValidPqcKemKeypairShape(keypair) {
|
|
35
|
+
return keypair.secretKey.length === ml_kem1024.lengths.secretKey && keypair.publicKey.length === ml_kem1024.lengths.publicKey;
|
|
36
|
+
}
|
|
28
37
|
//# sourceMappingURL=pqc.js.map
|
package/dist/crypto/pqc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pqc.js","sourceRoot":"","sources":["../../src/crypto/pqc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAY3D,uFAAuF;AACvF,MAAM,UAAU,sBAAsB;IACpC,OAAO,QAAQ,CAAC,MAAM,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAmB,EAAE,SAAqB;
|
|
1
|
+
{"version":3,"file":"pqc.js","sourceRoot":"","sources":["../../src/crypto/pqc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAY3D,uFAAuF;AACvF,MAAM,UAAU,sBAAsB;IACpC,OAAO,QAAQ,CAAC,MAAM,EAAE,CAAC;AAC3B,CAAC;AAED,gNAAgN;AAChN,MAAM,UAAU,OAAO,CAAC,OAAmB,EAAE,SAAqB,EAAE,OAAoB;IACtF,OAAO,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,SAAqB,EAAE,OAAmB,EAAE,SAAqB,EAAE,OAAoB;IAC/G,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC3F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,qBAAqB;IACnC,OAAO,UAAU,CAAC,MAAM,EAAE,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,cAA0B;IACvD,OAAO,UAAU,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAAsB,EAAE,SAAqB;IAC1E,OAAO,UAAU,CAAC,WAAW,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AACvD,CAAC;AAED,0OAA0O;AAC1O,MAAM,UAAU,0BAA0B,CAAC,OAAuB;IAChE,OAAO,OAAO,CAAC,SAAS,CAAC,MAAM,KAAK,QAAQ,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,KAAK,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC;AAC5H,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,yBAAyB,CAAC,OAAsB;IAC9D,OAAO,OAAO,CAAC,SAAS,CAAC,MAAM,KAAK,UAAU,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,KAAK,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC;AAChI,CAAC"}
|
package/dist/crypto/ratchet.js
CHANGED
|
@@ -9,6 +9,11 @@ import { classicalSharedSecret } from "./classical.js";
|
|
|
9
9
|
// group/multi-device fan-out — this stays two-party-only and in-order-only,
|
|
10
10
|
// matching this project's always-ordered, always-1:1 transport).
|
|
11
11
|
//
|
|
12
|
+
// Callers should hold ONE ROOT PER DIRECTION (a send root advanced only by
|
|
13
|
+
// their own steps, a receive root advanced only by mirroring the peer's) and
|
|
14
|
+
// say explicitly on the wire when a step happened and which peer key it used.
|
|
15
|
+
// A single shared root diverges when both sides step at once — see ADR 0013.
|
|
16
|
+
//
|
|
12
17
|
// Everything here is a PURE function: no "when do I step" logic, no
|
|
13
18
|
// connection-level state, no wire format. The caller (`secureConnection.ts`'s
|
|
14
19
|
// Envelope layer, a later, currently-blocked ticket) drives all of that.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ratchet.js","sourceRoot":"","sources":["../../src/crypto/ratchet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAyB,MAAM,gBAAgB,CAAC;AAE9E,0EAA0E;AAC1E,6EAA6E;AAC7E,6EAA6E;AAC7E,sEAAsE;AACtE,4EAA4E;AAC5E,iEAAiE;AACjE,EAAE;AACF,oEAAoE;AACpE,8EAA8E;AAC9E,yEAAyE;AAEzE,MAAM,iBAAiB,GAAG,WAAW,CAAC,+BAA+B,CAAC,CAAC;AACvE,MAAM,gBAAgB,GAAG,WAAW,CAAC,oCAAoC,CAAC,CAAC;AAC3E,MAAM,kBAAkB,GAAG,WAAW,CAAC,sCAAsC,CAAC,CAAC;AAC/E,MAAM,uBAAuB,GAAG,WAAW,CAAC,2CAA2C,CAAC,CAAC;AACzF,MAAM,sBAAsB,GAAG,WAAW,CAAC,0CAA0C,CAAC,CAAC;AAEvF,qKAAqK;AACrK,MAAM,CAAC,MAAM,iCAAiC,GAAG,WAAW,CAAC,iBAAiB,EAAE,WAAW,CAAC,wBAAwB,CAAC,CAAC,CAAC;AACvH,6CAA6C;AAC7C,MAAM,CAAC,MAAM,iCAAiC,GAAG,WAAW,CAAC,iBAAiB,EAAE,WAAW,CAAC,wBAAwB,CAAC,CAAC,CAAC;AAEvH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,OAAmB,EAAE,SAAqB;IACrE,OAAO,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAUD;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,QAAoB;IAClD,OAAO;QACL,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,EAAE,CAAC;QACnE,YAAY,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,kBAAkB,EAAE,EAAE,CAAC;KACxE,CAAC;AACJ,CAAC;AAQD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAmB,EACnB,SAA2B,EAC3B,6BAAyC,EACzC,gBAA4B;IAE5B,MAAM,YAAY,GAAG,qBAAqB,CAAC,SAAS,CAAC,SAAS,EAAE,6BAA6B,CAAC,CAAC;IAC/F,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,SAAS,EAAE,uBAAuB,EAAE,EAAE,CAAC,CAAC;IAC5G,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,CAAC,UAAU,EAAE,gBAAgB,CAAC,EAAE,CAAC;AAC9F,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,OAAmB,EAAE,SAAqB,EAAE,gBAA4B;IACrG,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,sBAAsB,EAAE,EAAE,CAAC,CAAC;IACxG,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,CAAC,UAAU,EAAE,gBAAgB,CAAC,EAAE,CAAC;AAC9F,CAAC"}
|
|
1
|
+
{"version":3,"file":"ratchet.js","sourceRoot":"","sources":["../../src/crypto/ratchet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAyB,MAAM,gBAAgB,CAAC;AAE9E,0EAA0E;AAC1E,6EAA6E;AAC7E,6EAA6E;AAC7E,sEAAsE;AACtE,4EAA4E;AAC5E,iEAAiE;AACjE,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,8EAA8E;AAC9E,6EAA6E;AAC7E,EAAE;AACF,oEAAoE;AACpE,8EAA8E;AAC9E,yEAAyE;AAEzE,MAAM,iBAAiB,GAAG,WAAW,CAAC,+BAA+B,CAAC,CAAC;AACvE,MAAM,gBAAgB,GAAG,WAAW,CAAC,oCAAoC,CAAC,CAAC;AAC3E,MAAM,kBAAkB,GAAG,WAAW,CAAC,sCAAsC,CAAC,CAAC;AAC/E,MAAM,uBAAuB,GAAG,WAAW,CAAC,2CAA2C,CAAC,CAAC;AACzF,MAAM,sBAAsB,GAAG,WAAW,CAAC,0CAA0C,CAAC,CAAC;AAEvF,qKAAqK;AACrK,MAAM,CAAC,MAAM,iCAAiC,GAAG,WAAW,CAAC,iBAAiB,EAAE,WAAW,CAAC,wBAAwB,CAAC,CAAC,CAAC;AACvH,6CAA6C;AAC7C,MAAM,CAAC,MAAM,iCAAiC,GAAG,WAAW,CAAC,iBAAiB,EAAE,WAAW,CAAC,wBAAwB,CAAC,CAAC,CAAC;AAEvH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,OAAmB,EAAE,SAAqB;IACrE,OAAO,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAUD;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,QAAoB;IAClD,OAAO;QACL,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,EAAE,CAAC;QACnE,YAAY,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,kBAAkB,EAAE,EAAE,CAAC;KACxE,CAAC;AACJ,CAAC;AAQD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAmB,EACnB,SAA2B,EAC3B,6BAAyC,EACzC,gBAA4B;IAE5B,MAAM,YAAY,GAAG,qBAAqB,CAAC,SAAS,CAAC,SAAS,EAAE,6BAA6B,CAAC,CAAC;IAC/F,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,SAAS,EAAE,uBAAuB,EAAE,EAAE,CAAC,CAAC;IAC5G,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,CAAC,UAAU,EAAE,gBAAgB,CAAC,EAAE,CAAC;AAC9F,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,OAAmB,EAAE,SAAqB,EAAE,gBAA4B;IACrG,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,sBAAsB,EAAE,EAAE,CAAC,CAAC;IACxG,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,CAAC,UAAU,EAAE,gBAAgB,CAAC,EAAE,CAAC;AAC9F,CAAC"}
|
package/dist/fileCrypto.d.ts
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export declare
|
|
1
|
+
/** Cost used for newly written files. Stored in each file, so it can be raised without a migration. */
|
|
2
|
+
export declare const DEFAULT_KDF: {
|
|
3
|
+
readonly name: "scrypt";
|
|
4
|
+
readonly N: number;
|
|
5
|
+
readonly r: 8;
|
|
6
|
+
readonly p: 1;
|
|
7
|
+
};
|
|
3
8
|
export declare class DecryptionFailedError extends Error {
|
|
4
9
|
constructor();
|
|
5
10
|
}
|
|
11
|
+
/** The file was written by a newer raz-encrypt than this one understands. */
|
|
12
|
+
export declare class UnsupportedFileVersionError extends Error {
|
|
13
|
+
constructor(version: unknown);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Atomically replaces `filePath` with `contents` (mode 0o600): write a temp
|
|
17
|
+
* file in the same directory, fsync it, rename it over the target, then
|
|
18
|
+
* best-effort fsync the directory. A crash at any point leaves either the
|
|
19
|
+
* complete old file or the complete new file — never a partial one.
|
|
20
|
+
*/
|
|
21
|
+
export declare function writeFileAtomic(filePath: string, contents: string, beforeRename?: () => Promise<void>): Promise<void>;
|
|
22
|
+
/** Encrypts `data` (any JSON-serializable value) at rest under a passphrase-derived AES-256-GCM key, in the current (v2) format, atomically. */
|
|
23
|
+
export declare function encryptToFile(filePath: string, passphrase: string, data: unknown): Promise<void>;
|
|
24
|
+
export interface DecryptedFile<T> {
|
|
25
|
+
data: T;
|
|
26
|
+
/** True if the file on disk is the legacy v1 format (a caller may upgrade it with `upgradeLegacyFile`). */
|
|
27
|
+
legacy: boolean;
|
|
28
|
+
/** Index into the candidate passphrases that actually unlocked the file (0 = the first/preferred one). */
|
|
29
|
+
passphraseIndex: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Decrypts a file of any supported version and reports whether it was the
|
|
33
|
+
* legacy format and which candidate passphrase worked. `passphrases` are tried
|
|
34
|
+
* in order (callers pass the normalized form first, then the raw form for
|
|
35
|
+
* files written before normalization existed). A missing file rejects with
|
|
36
|
+
* the filesystem's ENOENT error.
|
|
37
|
+
*/
|
|
38
|
+
export declare function decryptFileWithFormat<T>(filePath: string, passphrases: string | string[]): Promise<DecryptedFile<T>>;
|
|
6
39
|
export declare function decryptFromFile<T>(filePath: string, passphrase: string): Promise<T>;
|
|
40
|
+
/**
|
|
41
|
+
* Rewrites a file in the current format, under `passphrase` (used for a
|
|
42
|
+
* legacy v1 file, or one unlocked by a pre-normalization passphrase). Safe by construction:
|
|
43
|
+
* the new file is written to a temp path, re-read and decrypted with the same
|
|
44
|
+
* passphrase to prove it round-trips to `data`, and only then renamed over
|
|
45
|
+
* the original — so the old file stays intact until the instant the verified
|
|
46
|
+
* replacement atomically takes its place.
|
|
47
|
+
*/
|
|
48
|
+
export declare function upgradeLegacyFile(filePath: string, passphrase: string, data: unknown): Promise<void>;
|
package/dist/fileCrypto.js
CHANGED
|
@@ -1,45 +1,214 @@
|
|
|
1
1
|
import { gcm } from "@noble/ciphers/aes.js";
|
|
2
|
-
import {
|
|
2
|
+
import { scryptAsync } from "@noble/hashes/scrypt.js";
|
|
3
3
|
import { bytesToHex, hexToBytes, randomBytes, utf8ToBytes } from "@noble/hashes/utils.js";
|
|
4
|
-
import { mkdir, readFile,
|
|
4
|
+
import { mkdir, open, readFile, rename, rm } from "node:fs/promises";
|
|
5
5
|
import { dirname } from "node:path";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
6
|
+
import { canonicalize } from "./canonicalJson.js";
|
|
7
|
+
// Encrypted-at-rest file format (ADR 0016).
|
|
8
|
+
//
|
|
9
|
+
// v2 (current): { version: 2, kdf: { name: "scrypt", N, r, p }, salt, nonce, ciphertext }
|
|
10
|
+
// - the KDF parameters live in the file, so the cost can be raised later
|
|
11
|
+
// without breaking files written today;
|
|
12
|
+
// - the header (version, kdf, salt, nonce) is authenticated as AES-GCM AAD;
|
|
13
|
+
// - written atomically (temp file + fsync + rename).
|
|
14
|
+
// v1 (legacy, read-only): { salt, nonce, ciphertext } with fixed scrypt
|
|
15
|
+
// N=2^17 r=8 p=1 and no AAD. Still readable forever; callers may upgrade it.
|
|
16
|
+
/** Cost used for newly written files. Stored in each file, so it can be raised without a migration. */
|
|
17
|
+
export const DEFAULT_KDF = { name: "scrypt", N: 2 ** 17, r: 8, p: 1 };
|
|
18
|
+
const DK_LEN = 32;
|
|
19
|
+
/** The only parameters v1 files could ever have used. */
|
|
20
|
+
const LEGACY_KDF = { name: "scrypt", N: 2 ** 17, r: 8, p: 1 };
|
|
21
|
+
// A file we are asked to open must not be able to make us allocate unbounded
|
|
22
|
+
// memory (scrypt needs ~128*N*r bytes) or burn unbounded CPU.
|
|
23
|
+
const MIN_N = 2 ** 14;
|
|
24
|
+
const MAX_N = 2 ** 20;
|
|
25
|
+
const MAX_N_TIMES_R = 2 ** 23; // ~1 GiB of scrypt memory
|
|
26
|
+
const MAX_R = 16;
|
|
27
|
+
const MAX_P = 4;
|
|
25
28
|
export class DecryptionFailedError extends Error {
|
|
26
29
|
constructor() {
|
|
27
30
|
super("Failed to decrypt local store: wrong passphrase or corrupted/tampered file.");
|
|
28
31
|
this.name = "DecryptionFailedError";
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
|
-
|
|
34
|
+
/** The file was written by a newer raz-encrypt than this one understands. */
|
|
35
|
+
export class UnsupportedFileVersionError extends Error {
|
|
36
|
+
constructor(version) {
|
|
37
|
+
super(`Local store has format version ${String(version)}, which this version of raz-encrypt does not understand. Upgrade raz-encrypt.`);
|
|
38
|
+
this.name = "UnsupportedFileVersionError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function isPowerOfTwo(n) {
|
|
42
|
+
return Number.isInteger(n) && n > 0 && (n & (n - 1)) === 0;
|
|
43
|
+
}
|
|
44
|
+
/** Rejects KDF parameters outside the range this library would ever write, before spending any memory or CPU on them. */
|
|
45
|
+
function validateKdf(kdf) {
|
|
46
|
+
const k = kdf;
|
|
47
|
+
const ok = typeof k === "object" &&
|
|
48
|
+
k !== null &&
|
|
49
|
+
k.name === "scrypt" &&
|
|
50
|
+
isPowerOfTwo(k.N) &&
|
|
51
|
+
k.N >= MIN_N &&
|
|
52
|
+
k.N <= MAX_N &&
|
|
53
|
+
Number.isInteger(k.r) &&
|
|
54
|
+
k.r >= 1 &&
|
|
55
|
+
k.r <= MAX_R &&
|
|
56
|
+
Number.isInteger(k.p) &&
|
|
57
|
+
k.p >= 1 &&
|
|
58
|
+
k.p <= MAX_P &&
|
|
59
|
+
k.N * k.r <= MAX_N_TIMES_R;
|
|
60
|
+
if (!ok)
|
|
61
|
+
throw new DecryptionFailedError();
|
|
62
|
+
}
|
|
63
|
+
async function deriveKey(passphrase, salt, kdf) {
|
|
64
|
+
return scryptAsync(utf8ToBytes(passphrase), salt, { N: kdf.N, r: kdf.r, p: kdf.p, dkLen: DK_LEN });
|
|
65
|
+
}
|
|
66
|
+
/** The authenticated header bytes: everything in the file except the ciphertext itself. */
|
|
67
|
+
function headerAad(file) {
|
|
68
|
+
return utf8ToBytes(canonicalize({ version: file.version, kdf: file.kdf, salt: file.salt, nonce: file.nonce }));
|
|
69
|
+
}
|
|
70
|
+
async function encryptToString(passphrase, data) {
|
|
71
|
+
const salt = randomBytes(16);
|
|
72
|
+
const nonce = randomBytes(12);
|
|
73
|
+
const kdf = { ...DEFAULT_KDF };
|
|
74
|
+
const key = await deriveKey(passphrase, salt, kdf);
|
|
75
|
+
const header = { version: 2, kdf, salt: bytesToHex(salt), nonce: bytesToHex(nonce) };
|
|
76
|
+
const ciphertext = gcm(key, nonce, headerAad(header)).encrypt(utf8ToBytes(JSON.stringify(data)));
|
|
77
|
+
const file = { ...header, ciphertext: bytesToHex(ciphertext) };
|
|
78
|
+
return JSON.stringify(file);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Atomically replaces `filePath` with `contents` (mode 0o600): write a temp
|
|
82
|
+
* file in the same directory, fsync it, rename it over the target, then
|
|
83
|
+
* best-effort fsync the directory. A crash at any point leaves either the
|
|
84
|
+
* complete old file or the complete new file — never a partial one.
|
|
85
|
+
*/
|
|
86
|
+
export async function writeFileAtomic(filePath, contents, beforeRename) {
|
|
87
|
+
const dir = dirname(filePath);
|
|
88
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
89
|
+
const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
90
|
+
try {
|
|
91
|
+
const handle = await open(tmpPath, "w", 0o600);
|
|
92
|
+
try {
|
|
93
|
+
await handle.writeFile(contents, "utf8");
|
|
94
|
+
await handle.sync();
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
await handle.close();
|
|
98
|
+
}
|
|
99
|
+
if (beforeRename)
|
|
100
|
+
await beforeRename();
|
|
101
|
+
await rename(tmpPath, filePath);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
await rm(tmpPath, { force: true });
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const dirHandle = await open(dir, "r");
|
|
109
|
+
try {
|
|
110
|
+
await dirHandle.sync();
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
await dirHandle.close();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Directory fsync is best-effort (unsupported on some platforms/filesystems).
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Encrypts `data` (any JSON-serializable value) at rest under a passphrase-derived AES-256-GCM key, in the current (v2) format, atomically. */
|
|
121
|
+
export async function encryptToFile(filePath, passphrase, data) {
|
|
122
|
+
await writeFileAtomic(filePath, await encryptToString(passphrase, data));
|
|
123
|
+
}
|
|
124
|
+
async function decryptParsed(payload, passphrases) {
|
|
125
|
+
if (typeof payload !== "object" || payload === null)
|
|
126
|
+
throw new DecryptionFailedError();
|
|
127
|
+
const version = payload.version;
|
|
128
|
+
let salt;
|
|
129
|
+
let nonce;
|
|
130
|
+
let ciphertext;
|
|
131
|
+
let kdf;
|
|
132
|
+
let aad;
|
|
133
|
+
let legacy;
|
|
134
|
+
try {
|
|
135
|
+
if (version === undefined) {
|
|
136
|
+
const v1 = payload;
|
|
137
|
+
salt = hexToBytes(v1.salt);
|
|
138
|
+
nonce = hexToBytes(v1.nonce);
|
|
139
|
+
ciphertext = hexToBytes(v1.ciphertext);
|
|
140
|
+
kdf = { ...LEGACY_KDF };
|
|
141
|
+
aad = undefined;
|
|
142
|
+
legacy = true;
|
|
143
|
+
}
|
|
144
|
+
else if (version === 2) {
|
|
145
|
+
const v2 = payload;
|
|
146
|
+
validateKdf(v2.kdf);
|
|
147
|
+
salt = hexToBytes(v2.salt);
|
|
148
|
+
nonce = hexToBytes(v2.nonce);
|
|
149
|
+
ciphertext = hexToBytes(v2.ciphertext);
|
|
150
|
+
kdf = v2.kdf;
|
|
151
|
+
aad = headerAad(v2);
|
|
152
|
+
legacy = false;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
throw new UnsupportedFileVersionError(version);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
if (err instanceof UnsupportedFileVersionError)
|
|
160
|
+
throw err;
|
|
161
|
+
throw new DecryptionFailedError();
|
|
162
|
+
}
|
|
163
|
+
for (let passphraseIndex = 0; passphraseIndex < passphrases.length; passphraseIndex++) {
|
|
164
|
+
const key = await deriveKey(passphrases[passphraseIndex], salt, kdf);
|
|
165
|
+
try {
|
|
166
|
+
const plaintext = gcm(key, nonce, aad).decrypt(ciphertext);
|
|
167
|
+
return { data: JSON.parse(new TextDecoder().decode(plaintext)), legacy, passphraseIndex };
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Try the next candidate passphrase, if any.
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
throw new DecryptionFailedError();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Decrypts a file of any supported version and reports whether it was the
|
|
177
|
+
* legacy format and which candidate passphrase worked. `passphrases` are tried
|
|
178
|
+
* in order (callers pass the normalized form first, then the raw form for
|
|
179
|
+
* files written before normalization existed). A missing file rejects with
|
|
180
|
+
* the filesystem's ENOENT error.
|
|
181
|
+
*/
|
|
182
|
+
export async function decryptFileWithFormat(filePath, passphrases) {
|
|
32
183
|
const raw = await readFile(filePath, "utf8");
|
|
33
|
-
|
|
34
|
-
const salt = hexToBytes(payload.salt);
|
|
35
|
-
const nonce = hexToBytes(payload.nonce);
|
|
36
|
-
const key = deriveKey(passphrase, salt);
|
|
184
|
+
let payload;
|
|
37
185
|
try {
|
|
38
|
-
|
|
39
|
-
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
186
|
+
payload = JSON.parse(raw);
|
|
40
187
|
}
|
|
41
188
|
catch {
|
|
42
189
|
throw new DecryptionFailedError();
|
|
43
190
|
}
|
|
191
|
+
return (await decryptParsed(payload, Array.isArray(passphrases) ? passphrases : [passphrases]));
|
|
192
|
+
}
|
|
193
|
+
export async function decryptFromFile(filePath, passphrase) {
|
|
194
|
+
return (await decryptFileWithFormat(filePath, passphrase)).data;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Rewrites a file in the current format, under `passphrase` (used for a
|
|
198
|
+
* legacy v1 file, or one unlocked by a pre-normalization passphrase). Safe by construction:
|
|
199
|
+
* the new file is written to a temp path, re-read and decrypted with the same
|
|
200
|
+
* passphrase to prove it round-trips to `data`, and only then renamed over
|
|
201
|
+
* the original — so the old file stays intact until the instant the verified
|
|
202
|
+
* replacement atomically takes its place.
|
|
203
|
+
*/
|
|
204
|
+
export async function upgradeLegacyFile(filePath, passphrase, data) {
|
|
205
|
+
const contents = await encryptToString(passphrase, data);
|
|
206
|
+
const expected = JSON.stringify(data);
|
|
207
|
+
await writeFileAtomic(filePath, contents, async () => {
|
|
208
|
+
// Verify the exact bytes we are about to publish, with the real passphrase.
|
|
209
|
+
const check = await decryptParsed(JSON.parse(contents), [passphrase]);
|
|
210
|
+
if (JSON.stringify(check.data) !== expected)
|
|
211
|
+
throw new Error("Upgraded identity file failed its round-trip check; leaving the original untouched.");
|
|
212
|
+
});
|
|
44
213
|
}
|
|
45
214
|
//# sourceMappingURL=fileCrypto.js.map
|
package/dist/fileCrypto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileCrypto.js","sourceRoot":"","sources":["../src/fileCrypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAC5C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"fileCrypto.js","sourceRoot":"","sources":["../src/fileCrypto.ts"],"names":[],"mappings":"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"}
|
package/dist/identity.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type ClassicalKeypair } from "./crypto/classical.js";
|
|
2
|
+
import { type Certificate, type SignOptions } from "./crypto/hybrid.js";
|
|
3
|
+
import { MIN_PASSPHRASE_LENGTH, type PassphraseChecker } from "./passphrase.js";
|
|
2
4
|
import { type PqcKemKeypair, type PqcSignKeypair } from "./crypto/pqc.js";
|
|
3
5
|
export interface Identity {
|
|
4
6
|
alias: string;
|
|
@@ -14,22 +16,31 @@ export interface PublicCard {
|
|
|
14
16
|
pqcSignPublicKey: string;
|
|
15
17
|
pqcKemPublicKey: string;
|
|
16
18
|
}
|
|
17
|
-
export
|
|
19
|
+
export { MIN_PASSPHRASE_LENGTH };
|
|
18
20
|
export interface CreateIdentityOptions {
|
|
19
21
|
storageDir: string;
|
|
20
22
|
passphrase: string;
|
|
21
23
|
alias: string;
|
|
22
24
|
/** Overwrite an existing identity at this storageDir. Defaults to false. */
|
|
23
25
|
overwrite?: boolean;
|
|
26
|
+
/** Optional, runs after the built-in checks below. See PassphraseChecker (ADR 0021). */
|
|
27
|
+
checkPassphrase?: PassphraseChecker;
|
|
24
28
|
}
|
|
25
29
|
/**
|
|
26
30
|
* Generates a new hybrid classical+post-quantum identity (one ECDSA/ECDH-P384
|
|
27
31
|
* keypair, one ML-DSA-87 signing keypair, one ML-KEM-1024 key-exchange
|
|
28
32
|
* keypair) and persists it encrypted-at-rest under `storageDir`.
|
|
29
33
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
34
|
+
* The passphrase is normalized to Unicode NFKC, must be at least
|
|
35
|
+
* MIN_PASSPHRASE_LENGTH characters (counted as code points after
|
|
36
|
+
* normalization), must not be a single repeated character, and must not be
|
|
37
|
+
* on a small blocklist of very common passphrases (ADR 0017). These checks
|
|
38
|
+
* only apply here, not to loadIdentity, so an identity created before they
|
|
39
|
+
* existed can still be loaded with its original passphrase.
|
|
40
|
+
*
|
|
41
|
+
* `options.checkPassphrase`, if supplied, runs after all of the above pass
|
|
42
|
+
* (ADR 0021) — this is where real breach-corpus checking belongs, not in
|
|
43
|
+
* this library's own, deliberately small, blocklist.
|
|
33
44
|
*/
|
|
34
45
|
export declare function createIdentity(options: CreateIdentityOptions): Promise<Identity>;
|
|
35
46
|
export interface LoadIdentityOptions {
|
|
@@ -38,5 +49,59 @@ export interface LoadIdentityOptions {
|
|
|
38
49
|
}
|
|
39
50
|
/** Loads a previously-created identity back from disk. */
|
|
40
51
|
export declare function loadIdentity(options: LoadIdentityOptions): Promise<Identity>;
|
|
52
|
+
/**
|
|
53
|
+
* Supplies an Identity's secret-key material from somewhere other than
|
|
54
|
+
* `identity.enc.json` — an OS keychain, a TPM, an HSM, anything (issue
|
|
55
|
+
* #38/ADR 0024). Scoped to key SOURCING, not operation delegation: once
|
|
56
|
+
* `load()` returns, this library operates on the returned Uint8Arrays
|
|
57
|
+
* directly in its own process memory exactly as it does for a file-loaded
|
|
58
|
+
* Identity. There is no way to make classicalSign/pqcSign/etc. forward the
|
|
59
|
+
* actual signing operation to the device instead — see ADR 0024 for why
|
|
60
|
+
* that's out of scope (and, for the post-quantum half, not achievable with
|
|
61
|
+
* any hardware that exists today regardless).
|
|
62
|
+
*/
|
|
63
|
+
export interface KeyStore {
|
|
64
|
+
load(): Promise<{
|
|
65
|
+
classical: ClassicalKeypair;
|
|
66
|
+
pqcSign: PqcSignKeypair;
|
|
67
|
+
pqcKem: PqcKemKeypair;
|
|
68
|
+
}>;
|
|
69
|
+
}
|
|
70
|
+
export interface LoadIdentityFromKeyStoreOptions {
|
|
71
|
+
alias: string;
|
|
72
|
+
storageDir: string;
|
|
73
|
+
keyStore: KeyStore;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Builds an Identity from a caller-supplied KeyStore instead of a
|
|
77
|
+
* passphrase-encrypted file — `identity.enc.json` and a passphrase are
|
|
78
|
+
* never part of the picture. The returned key material's shape (byte
|
|
79
|
+
* lengths) is validated before trusting it, so a buggy KeyStore fails
|
|
80
|
+
* clearly here rather than with a cryptic error the first time a signing
|
|
81
|
+
* call happens to touch the bad bytes. `storageDir` is still required: the
|
|
82
|
+
* trust store and audit log are a separate concern from where secret keys
|
|
83
|
+
* live, unaffected by this function.
|
|
84
|
+
*/
|
|
85
|
+
export declare function loadIdentityFromKeyStore(options: LoadIdentityFromKeyStoreOptions): Promise<Identity>;
|
|
86
|
+
/**
|
|
87
|
+
* Best-effort wipe of the secret key bytes held by `identity`: overwrites
|
|
88
|
+
* them with zeros so they do not linger in this process's memory after you
|
|
89
|
+
* are done. The identity is unusable afterwards. Limits: JavaScript gives no
|
|
90
|
+
* guarantee about copies the engine or GC already made, and the hex strings
|
|
91
|
+
* produced while saving or loading an identity are immutable and cannot be
|
|
92
|
+
* wiped. This narrows the exposure window; it is not a secure-erase.
|
|
93
|
+
*/
|
|
94
|
+
export declare function zeroizeIdentity(identity: Identity): void;
|
|
41
95
|
/** The shareable public half of an identity — hand this to the other party out of band. */
|
|
42
96
|
export declare function exportPublicIdentity(identity: Identity): PublicCard;
|
|
97
|
+
/**
|
|
98
|
+
* A self-signed, optionally time-limited version of `exportPublicIdentity`'s
|
|
99
|
+
* card (ADR 0023/issue #37): the identity signs its own card, so a peer can
|
|
100
|
+
* verify it wasn't tampered with and — if `options.expiresAt` was set —
|
|
101
|
+
* reject it once expired, enforcing a cryptoperiod the bare card has no way
|
|
102
|
+
* to carry (it has no signature at all). `exportPublicIdentity` itself is
|
|
103
|
+
* unchanged; this is a separate, opt-in function precisely so every
|
|
104
|
+
* existing caller of the bare card is unaffected. Verify and trust a
|
|
105
|
+
* received one with `trustSignedCard`, not `trustParty`.
|
|
106
|
+
*/
|
|
107
|
+
export declare function exportSignedCard(identity: Identity, options?: SignOptions): Promise<Certificate>;
|