raz-encrypt 2.1.0 → 2.3.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 +448 -442
- package/dist/canonicalJson.d.ts +0 -15
- package/dist/canonicalJson.js +13 -73
- package/dist/canonicalJson.js.map +1 -1
- package/dist/core.d.ts +2 -4
- package/dist/core.js +1 -3
- package/dist/core.js.map +1 -1
- package/dist/crypto/classical.d.ts +0 -2
- package/dist/crypto/classical.js +0 -4
- package/dist/crypto/classical.js.map +1 -1
- package/dist/crypto/frameCipher.js +2 -8
- package/dist/crypto/frameCipher.js.map +1 -1
- package/dist/crypto/hybrid.d.ts +8 -95
- package/dist/crypto/hybrid.js +49 -374
- package/dist/crypto/hybrid.js.map +1 -1
- package/dist/crypto/nodeHybrid.d.ts +4 -6
- package/dist/crypto/nodeHybrid.js +2 -3
- package/dist/crypto/nodeHybrid.js.map +1 -1
- package/dist/crypto/pqc.d.ts +2 -7
- package/dist/crypto/pqc.js +4 -13
- package/dist/crypto/pqc.js.map +1 -1
- package/dist/crypto/ratchet.js +0 -5
- package/dist/crypto/ratchet.js.map +1 -1
- package/dist/fileCrypto.d.ts +2 -44
- package/dist/fileCrypto.js +28 -197
- package/dist/fileCrypto.js.map +1 -1
- package/dist/identity.d.ts +4 -69
- package/dist/identity.js +12 -111
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +7 -8
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/store.d.ts +5 -42
- package/dist/store.js +22 -254
- package/dist/store.js.map +1 -1
- package/dist/trust.d.ts +2 -143
- package/dist/trust.js +27 -376
- package/dist/trust.js.map +1 -1
- package/package.json +69 -62
- package/dist/crypto/confirmedSession.d.ts +0 -47
- package/dist/crypto/confirmedSession.js +0 -84
- package/dist/crypto/confirmedSession.js.map +0 -1
- package/dist/crypto/frameSession.d.ts +0 -40
- package/dist/crypto/frameSession.js +0 -96
- package/dist/crypto/frameSession.js.map +0 -1
- package/dist/passphrase.d.ts +0 -27
- package/dist/passphrase.js +0 -69
- package/dist/passphrase.js.map +0 -1
|
@@ -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
|
|
14
|
-
export {
|
|
15
|
-
export type {
|
|
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";
|
|
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,6 +39,4 @@ 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
|
|
43
|
-
isReplay?: DecryptDeps["isReplay"];
|
|
44
|
-
}): Promise<T>;
|
|
42
|
+
export declare function decryptFrom<T = unknown>(identity: Identity, counterpart: PublicCard, box: EncryptedBox): 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 {
|
|
4
|
+
export { 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,9 +56,8 @@ 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) {
|
|
60
60
|
return decryptFromCore(identity, counterpart, box, {
|
|
61
|
-
isReplay: options.isReplay,
|
|
62
61
|
checkTrust: (card) => trustStatusForCard(identity, card),
|
|
63
62
|
onLog: (entry) => appendLogEntry(identity, entry),
|
|
64
63
|
});
|
|
@@ -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,9 +8,8 @@ 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
|
|
13
|
-
export declare function pqcVerify(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array, context?: Uint8Array): boolean;
|
|
11
|
+
export declare function pqcSign(message: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
12
|
+
export declare function pqcVerify(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean;
|
|
14
13
|
/** ML-KEM-1024 (FIPS 203, category-5 parameter set) — the post-quantum key-exchange layer. */
|
|
15
14
|
export declare function generatePqcKemKeypair(): PqcKemKeypair;
|
|
16
15
|
export declare function pqcEncapsulate(theirPublicKey: Uint8Array): {
|
|
@@ -18,7 +17,3 @@ export declare function pqcEncapsulate(theirPublicKey: Uint8Array): {
|
|
|
18
17
|
sharedSecret: Uint8Array;
|
|
19
18
|
};
|
|
20
19
|
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,13 +4,12 @@ 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
|
-
|
|
9
|
-
return ml_dsa87.sign(message, secretKey, context ? { context } : undefined);
|
|
7
|
+
export function pqcSign(message, secretKey) {
|
|
8
|
+
return ml_dsa87.sign(message, secretKey);
|
|
10
9
|
}
|
|
11
|
-
export function pqcVerify(signature, message, publicKey
|
|
10
|
+
export function pqcVerify(signature, message, publicKey) {
|
|
12
11
|
try {
|
|
13
|
-
return ml_dsa87.verify(signature, message, publicKey
|
|
12
|
+
return ml_dsa87.verify(signature, message, publicKey);
|
|
14
13
|
}
|
|
15
14
|
catch {
|
|
16
15
|
return false;
|
|
@@ -26,12 +25,4 @@ export function pqcEncapsulate(theirPublicKey) {
|
|
|
26
25
|
export function pqcDecapsulate(cipherText, secretKey) {
|
|
27
26
|
return ml_kem1024.decapsulate(cipherText, secretKey);
|
|
28
27
|
}
|
|
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
|
-
}
|
|
37
28
|
//# 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,
|
|
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;IAChE,OAAO,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,SAAqB,EAAE,OAAmB,EAAE,SAAqB;IACzF,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IACxD,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"}
|
package/dist/crypto/ratchet.js
CHANGED
|
@@ -9,11 +9,6 @@ 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
|
-
//
|
|
17
12
|
// Everything here is a PURE function: no "when do I step" logic, no
|
|
18
13
|
// connection-level state, no wire format. The caller (`secureConnection.ts`'s
|
|
19
14
|
// 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,
|
|
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"}
|
package/dist/fileCrypto.d.ts
CHANGED
|
@@ -1,48 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export declare
|
|
3
|
-
readonly name: "scrypt";
|
|
4
|
-
readonly N: number;
|
|
5
|
-
readonly r: 8;
|
|
6
|
-
readonly p: 1;
|
|
7
|
-
};
|
|
1
|
+
/** Encrypts `data` (any JSON-serializable value) at rest under a passphrase-derived AES-256-GCM key. */
|
|
2
|
+
export declare function encryptToFile(filePath: string, passphrase: string, data: unknown): Promise<void>;
|
|
8
3
|
export declare class DecryptionFailedError extends Error {
|
|
9
4
|
constructor();
|
|
10
5
|
}
|
|
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>>;
|
|
39
6
|
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,214 +1,45 @@
|
|
|
1
1
|
import { gcm } from "@noble/ciphers/aes.js";
|
|
2
|
-
import {
|
|
2
|
+
import { scrypt } from "@noble/hashes/scrypt.js";
|
|
3
3
|
import { bytesToHex, hexToBytes, randomBytes, utf8ToBytes } from "@noble/hashes/utils.js";
|
|
4
|
-
import { mkdir,
|
|
4
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
5
5
|
import { dirname } from "node:path";
|
|
6
|
-
|
|
7
|
-
|
|
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;
|
|
28
|
-
export class DecryptionFailedError extends Error {
|
|
29
|
-
constructor() {
|
|
30
|
-
super("Failed to decrypt local store: wrong passphrase or corrupted/tampered file.");
|
|
31
|
-
this.name = "DecryptionFailedError";
|
|
32
|
-
}
|
|
33
|
-
}
|
|
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 }));
|
|
6
|
+
const SCRYPT_PARAMS = { N: 2 ** 17, r: 8, p: 1, dkLen: 32 };
|
|
7
|
+
function deriveKey(passphrase, salt) {
|
|
8
|
+
return scrypt(utf8ToBytes(passphrase), salt, SCRYPT_PARAMS);
|
|
69
9
|
}
|
|
70
|
-
|
|
10
|
+
/** Encrypts `data` (any JSON-serializable value) at rest under a passphrase-derived AES-256-GCM key. */
|
|
11
|
+
export async function encryptToFile(filePath, passphrase, data) {
|
|
71
12
|
const salt = randomBytes(16);
|
|
72
13
|
const nonce = randomBytes(12);
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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));
|
|
14
|
+
const key = deriveKey(passphrase, salt);
|
|
15
|
+
const plaintext = utf8ToBytes(JSON.stringify(data));
|
|
16
|
+
const ciphertext = gcm(key, nonce).encrypt(plaintext);
|
|
17
|
+
const payload = {
|
|
18
|
+
salt: bytesToHex(salt),
|
|
19
|
+
nonce: bytesToHex(nonce),
|
|
20
|
+
ciphertext: bytesToHex(ciphertext),
|
|
21
|
+
};
|
|
22
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
23
|
+
await writeFile(filePath, JSON.stringify(payload), { mode: 0o600 });
|
|
123
24
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
}
|
|
25
|
+
export class DecryptionFailedError extends Error {
|
|
26
|
+
constructor() {
|
|
27
|
+
super("Failed to decrypt local store: wrong passphrase or corrupted/tampered file.");
|
|
28
|
+
this.name = "DecryptionFailedError";
|
|
172
29
|
}
|
|
173
|
-
throw new DecryptionFailedError();
|
|
174
30
|
}
|
|
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) {
|
|
31
|
+
export async function decryptFromFile(filePath, passphrase) {
|
|
183
32
|
const raw = await readFile(filePath, "utf8");
|
|
184
|
-
|
|
33
|
+
const payload = JSON.parse(raw);
|
|
34
|
+
const salt = hexToBytes(payload.salt);
|
|
35
|
+
const nonce = hexToBytes(payload.nonce);
|
|
36
|
+
const key = deriveKey(passphrase, salt);
|
|
185
37
|
try {
|
|
186
|
-
|
|
38
|
+
const plaintext = gcm(key, nonce).decrypt(hexToBytes(payload.ciphertext));
|
|
39
|
+
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
187
40
|
}
|
|
188
41
|
catch {
|
|
189
42
|
throw new DecryptionFailedError();
|
|
190
43
|
}
|
|
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
|
-
});
|
|
213
44
|
}
|
|
214
45
|
//# 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":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,aAAa,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AAQ5D,SAAS,SAAS,CAAC,UAAkB,EAAE,IAAgB;IACrD,OAAO,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;AAC9D,CAAC;AAED,wGAAwG;AACxG,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,UAAkB,EAAE,IAAa;IACrF,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IACpD,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEtD,MAAM,OAAO,GAAkB;QAC7B,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC;QACtB,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC;QACxB,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC;KACnC,CAAC;IAEF,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C;QACE,KAAK,CAAC,6EAA6E,CAAC,CAAC;QACrF,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAI,QAAgB,EAAE,UAAkB;IAC3E,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAC;IACjD,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAExC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;QAC1E,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAM,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,qBAAqB,EAAE,CAAC;IACpC,CAAC;AACH,CAAC"}
|
package/dist/identity.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
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";
|
|
4
2
|
import { type PqcKemKeypair, type PqcSignKeypair } from "./crypto/pqc.js";
|
|
5
3
|
export interface Identity {
|
|
6
4
|
alias: string;
|
|
@@ -16,31 +14,22 @@ export interface PublicCard {
|
|
|
16
14
|
pqcSignPublicKey: string;
|
|
17
15
|
pqcKemPublicKey: string;
|
|
18
16
|
}
|
|
19
|
-
export
|
|
17
|
+
export declare const MIN_PASSPHRASE_LENGTH = 12;
|
|
20
18
|
export interface CreateIdentityOptions {
|
|
21
19
|
storageDir: string;
|
|
22
20
|
passphrase: string;
|
|
23
21
|
alias: string;
|
|
24
22
|
/** Overwrite an existing identity at this storageDir. Defaults to false. */
|
|
25
23
|
overwrite?: boolean;
|
|
26
|
-
/** Optional, runs after the built-in checks below. See PassphraseChecker (ADR 0021). */
|
|
27
|
-
checkPassphrase?: PassphraseChecker;
|
|
28
24
|
}
|
|
29
25
|
/**
|
|
30
26
|
* Generates a new hybrid classical+post-quantum identity (one ECDSA/ECDH-P384
|
|
31
27
|
* keypair, one ML-DSA-87 signing keypair, one ML-KEM-1024 key-exchange
|
|
32
28
|
* keypair) and persists it encrypted-at-rest under `storageDir`.
|
|
33
29
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
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.
|
|
30
|
+
* Requires a passphrase of at least MIN_PASSPHRASE_LENGTH characters — this
|
|
31
|
+
* check only applies here, not to loadIdentity, so an identity created
|
|
32
|
+
* before this check existed can still be loaded with its original passphrase.
|
|
44
33
|
*/
|
|
45
34
|
export declare function createIdentity(options: CreateIdentityOptions): Promise<Identity>;
|
|
46
35
|
export interface LoadIdentityOptions {
|
|
@@ -49,59 +38,5 @@ export interface LoadIdentityOptions {
|
|
|
49
38
|
}
|
|
50
39
|
/** Loads a previously-created identity back from disk. */
|
|
51
40
|
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;
|
|
95
41
|
/** The shareable public half of an identity — hand this to the other party out of band. */
|
|
96
42
|
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>;
|