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
package/dist/canonicalJson.d.ts
CHANGED
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Canonical JSON per RFC 8785 (JCS): no whitespace, object keys sorted by
|
|
3
|
-
* UTF-16 code unit, ECMAScript number and string serialization — so the same
|
|
4
|
-
* logical document always hashes to the same value regardless of key order
|
|
5
|
-
* on the wire.
|
|
6
|
-
*
|
|
7
|
-
* Unlike a bare `JSON.stringify`, this rejects (with a `TypeError` naming the
|
|
8
|
-
* offending path) every value that JSON cannot represent faithfully, rather
|
|
9
|
-
* than silently collapsing it into something else. Two different in-memory
|
|
10
|
-
* documents must never canonicalize to the same bytes (ADR 0012):
|
|
11
|
-
* `undefined`, `NaN`, `±Infinity`, `BigInt`, functions, symbols, sparse
|
|
12
|
-
* arrays, circular references, lone surrogates, and anything that is not a
|
|
13
|
-
* plain object / array / string / finite number / boolean / null (`Map`,
|
|
14
|
-
* `Set`, `Date`, typed arrays, class instances, ...).
|
|
15
|
-
*/
|
|
16
1
|
export declare function canonicalize(value: unknown): string;
|
|
17
2
|
/** The document hash every signature is made over. */
|
|
18
3
|
export declare function hashDocument(value: unknown): string;
|
package/dist/canonicalJson.js
CHANGED
|
@@ -1,82 +1,22 @@
|
|
|
1
1
|
import { sha512 } from "@noble/hashes/sha2.js";
|
|
2
2
|
import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* on the wire.
|
|
8
|
-
*
|
|
9
|
-
* Unlike a bare `JSON.stringify`, this rejects (with a `TypeError` naming the
|
|
10
|
-
* offending path) every value that JSON cannot represent faithfully, rather
|
|
11
|
-
* than silently collapsing it into something else. Two different in-memory
|
|
12
|
-
* documents must never canonicalize to the same bytes (ADR 0012):
|
|
13
|
-
* `undefined`, `NaN`, `±Infinity`, `BigInt`, functions, symbols, sparse
|
|
14
|
-
* arrays, circular references, lone surrogates, and anything that is not a
|
|
15
|
-
* plain object / array / string / finite number / boolean / null (`Map`,
|
|
16
|
-
* `Set`, `Date`, typed arrays, class instances, ...).
|
|
4
|
+
* Deterministic JSON serialization (object keys sorted recursively) so the
|
|
5
|
+
* same logical document always hashes to the same value regardless of key
|
|
6
|
+
* order on the wire.
|
|
17
7
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
switch (typeof value) {
|
|
26
|
-
case "string":
|
|
27
|
-
return serializeString(value, path);
|
|
28
|
-
case "number":
|
|
29
|
-
if (!Number.isFinite(value))
|
|
30
|
-
fail(path, `non-finite number (${String(value)})`);
|
|
31
|
-
// ECMAScript Number::toString is exactly what RFC 8785 §3.2.2.3 mandates
|
|
32
|
-
// (including "-0" -> "0").
|
|
33
|
-
return JSON.stringify(value);
|
|
34
|
-
case "boolean":
|
|
35
|
-
return value ? "true" : "false";
|
|
36
|
-
case "object":
|
|
37
|
-
break;
|
|
38
|
-
default:
|
|
39
|
-
return fail(path, typeof value === "undefined" ? "undefined" : `${typeof value}`);
|
|
40
|
-
}
|
|
41
|
-
if (value === null)
|
|
42
|
-
return "null";
|
|
43
|
-
const obj = value;
|
|
44
|
-
if (ancestors.has(obj))
|
|
45
|
-
fail(path, "circular reference");
|
|
46
|
-
ancestors.add(obj);
|
|
47
|
-
try {
|
|
48
|
-
if (Array.isArray(obj)) {
|
|
49
|
-
const parts = [];
|
|
50
|
-
for (let i = 0; i < obj.length; i++) {
|
|
51
|
-
if (!(i in obj))
|
|
52
|
-
fail(`${path}[${i}]`, "hole in sparse array");
|
|
53
|
-
parts.push(serialize(obj[i], `${path}[${i}]`, ancestors));
|
|
54
|
-
}
|
|
55
|
-
return `[${parts.join(",")}]`;
|
|
56
|
-
}
|
|
57
|
-
const proto = Object.getPrototypeOf(obj);
|
|
58
|
-
if (proto !== Object.prototype && proto !== null) {
|
|
59
|
-
fail(path, `non-plain object (${proto.constructor?.name ?? "unknown"})`);
|
|
60
|
-
}
|
|
61
|
-
const record = obj;
|
|
62
|
-
// Default sort() compares UTF-16 code units, which is the RFC 8785 order.
|
|
63
|
-
const keys = Object.keys(record).sort();
|
|
64
|
-
const parts = keys.map((key) => `${serializeString(key, `${path}.${key}`)}:${serialize(record[key], `${path}.${key}`, ancestors)}`);
|
|
65
|
-
return `{${parts.join(",")}}`;
|
|
66
|
-
}
|
|
67
|
-
finally {
|
|
68
|
-
ancestors.delete(obj);
|
|
8
|
+
function sortKeysDeep(value) {
|
|
9
|
+
if (Array.isArray(value))
|
|
10
|
+
return value.map(sortKeysDeep);
|
|
11
|
+
if (value !== null && typeof value === "object") {
|
|
12
|
+
return Object.fromEntries(Object.keys(value)
|
|
13
|
+
.sort()
|
|
14
|
+
.map((key) => [key, sortKeysDeep(value[key])]));
|
|
69
15
|
}
|
|
16
|
+
return value;
|
|
70
17
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
function serializeString(value, path) {
|
|
74
|
-
// RFC 8785 requires I-JSON: strings must be well-formed UTF-16. A lone
|
|
75
|
-
// surrogate has no UTF-8 encoding, so two distinct strings would otherwise
|
|
76
|
-
// both turn into U+FFFD when hashed.
|
|
77
|
-
if (LONE_SURROGATE.test(value))
|
|
78
|
-
fail(path, "string with a lone surrogate");
|
|
79
|
-
return JSON.stringify(value);
|
|
18
|
+
export function canonicalize(value) {
|
|
19
|
+
return JSON.stringify(sortKeysDeep(value));
|
|
80
20
|
}
|
|
81
21
|
/** The document hash every signature is made over. */
|
|
82
22
|
export function hashDocument(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canonicalJson.js","sourceRoot":"","sources":["../src/canonicalJson.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEjE
|
|
1
|
+
{"version":3,"file":"canonicalJson.js","sourceRoot":"","sources":["../src/canonicalJson.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;;;GAIG;AACH,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACzD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC;aAC1C,IAAI,EAAE;aACN,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,YAAY,CAAE,KAAiC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAC9E,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC"}
|
package/dist/core.d.ts
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* included, fs-backed versions of the same logic — this module is the
|
|
11
11
|
* un-wired core they're built from, not a separate implementation.
|
|
12
12
|
*/
|
|
13
|
-
export { createResponderHandshakeCore as createResponderHandshake, decryptFromCore as decryptFrom,
|
|
14
|
-
export type {
|
|
13
|
+
export { createResponderHandshakeCore as createResponderHandshake, decryptFromCore as decryptFrom, encryptForCore as encryptFor, establishSessionInitiatorCore as establishSessionInitiator, establishSessionResponderCore as establishSessionResponder, HandshakeVerificationError, signCore as sign, UntrustedPartyError, verifyCore as verify, } from "./crypto/hybrid.js";
|
|
14
|
+
export type { Certificate, EncryptedBox, HandshakeSignature, InitiatorHandshakeMessage, InitiatorSessionResult, LogDeps, ResponderHandshakeInit, ResponderHandshakeMessage, ResponderHandshakeSecrets, ResponderSessionResult, SessionKeys, SignOptions, TrustDeps, TrustStatus, VerifyDeps, VerifyFailureReason, VerifyResult, } from "./crypto/hybrid.js";
|
|
15
15
|
export { generateClassicalKeypair, classicalSharedSecret, classicalSign, classicalVerify } from "./crypto/classical.js";
|
|
16
16
|
export type { ClassicalKeypair } from "./crypto/classical.js";
|
|
17
17
|
export { generatePqcKemKeypair, generatePqcSignKeypair, pqcDecapsulate, pqcEncapsulate, pqcSign, pqcVerify, } from "./crypto/pqc.js";
|
|
@@ -20,8 +20,6 @@ export { canonicalize, hashDocument } from "./canonicalJson.js";
|
|
|
20
20
|
export { fingerprint } from "./fingerprint.js";
|
|
21
21
|
export { decryptFrame, encryptFrame, FrameDecryptionError } from "./crypto/frameCipher.js";
|
|
22
22
|
export type { EncryptedFrame } from "./crypto/frameCipher.js";
|
|
23
|
-
export { createFrameChannels, FrameCounterError, FrameReceiver, FrameSender } from "./crypto/frameSession.js";
|
|
24
|
-
export { ConfirmedSession, UnconfirmedSessionError } from "./crypto/confirmedSession.js";
|
|
25
23
|
export { CHAIN_INFO_INITIATOR_TO_RESPONDER, CHAIN_INFO_RESPONDER_TO_INITIATOR, ML_KEM_STEP_CADENCE, performEcdhStep, performKemStep, ratchetChainKey, seedChainKey, } from "./crypto/ratchet.js";
|
|
26
24
|
export type { AsymmetricStepResult, ChainStep } from "./crypto/ratchet.js";
|
|
27
25
|
export type { Identity, PublicCard } from "./identity.js";
|
package/dist/core.js
CHANGED
|
@@ -10,13 +10,11 @@
|
|
|
10
10
|
* included, fs-backed versions of the same logic — this module is the
|
|
11
11
|
* un-wired core they're built from, not a separate implementation.
|
|
12
12
|
*/
|
|
13
|
-
export { createResponderHandshakeCore as createResponderHandshake, decryptFromCore as decryptFrom,
|
|
13
|
+
export { createResponderHandshakeCore as createResponderHandshake, decryptFromCore as decryptFrom, encryptForCore as encryptFor, establishSessionInitiatorCore as establishSessionInitiator, establishSessionResponderCore as establishSessionResponder, HandshakeVerificationError, signCore as sign, UntrustedPartyError, verifyCore as verify, } from "./crypto/hybrid.js";
|
|
14
14
|
export { generateClassicalKeypair, classicalSharedSecret, classicalSign, classicalVerify } from "./crypto/classical.js";
|
|
15
15
|
export { generatePqcKemKeypair, generatePqcSignKeypair, pqcDecapsulate, pqcEncapsulate, pqcSign, pqcVerify, } from "./crypto/pqc.js";
|
|
16
16
|
export { canonicalize, hashDocument } from "./canonicalJson.js";
|
|
17
17
|
export { fingerprint } from "./fingerprint.js";
|
|
18
18
|
export { decryptFrame, encryptFrame, FrameDecryptionError } from "./crypto/frameCipher.js";
|
|
19
|
-
export { createFrameChannels, FrameCounterError, FrameReceiver, FrameSender } from "./crypto/frameSession.js";
|
|
20
|
-
export { ConfirmedSession, UnconfirmedSessionError } from "./crypto/confirmedSession.js";
|
|
21
19
|
export { CHAIN_INFO_INITIATOR_TO_RESPONDER, CHAIN_INFO_RESPONDER_TO_INITIATOR, ML_KEM_STEP_CADENCE, performEcdhStep, performKemStep, ratchetChainKey, seedChainKey, } from "./crypto/ratchet.js";
|
|
22
20
|
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,4BAA4B,IAAI,wBAAwB,EACxD,eAAe,IAAI,WAAW,EAC9B,
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,4BAA4B,IAAI,wBAAwB,EACxD,eAAe,IAAI,WAAW,EAC9B,cAAc,IAAI,UAAU,EAC5B,6BAA6B,IAAI,yBAAyB,EAC1D,6BAA6B,IAAI,yBAAyB,EAC1D,0BAA0B,EAC1B,QAAQ,IAAI,IAAI,EAChB,mBAAmB,EACnB,UAAU,IAAI,MAAM,GACrB,MAAM,oBAAoB,CAAC;AAqB5B,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxH,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,cAAc,EACd,OAAO,EACP,SAAS,GACV,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAG3F,OAAO,EACL,iCAAiC,EACjC,iCAAiC,EACjC,mBAAmB,EACnB,eAAe,EACf,cAAc,EACd,eAAe,EACf,YAAY,GACb,MAAM,qBAAqB,CAAC"}
|
|
@@ -8,5 +8,3 @@ export declare function classicalSign(message: Uint8Array, secretKey: Uint8Array
|
|
|
8
8
|
export declare function classicalVerify(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean;
|
|
9
9
|
/** ECDH shared secret between our secret key and their public key. */
|
|
10
10
|
export declare function classicalSharedSecret(secretKey: Uint8Array, theirPublicKey: Uint8Array): Uint8Array;
|
|
11
|
-
/** True iff `keypair`'s two fields are the exact byte lengths p384'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). */
|
|
12
|
-
export declare function isValidClassicalKeypairShape(keypair: ClassicalKeypair): boolean;
|
package/dist/crypto/classical.js
CHANGED
|
@@ -18,8 +18,4 @@ export function classicalVerify(signature, message, publicKey) {
|
|
|
18
18
|
export function classicalSharedSecret(secretKey, theirPublicKey) {
|
|
19
19
|
return p384.getSharedSecret(secretKey, theirPublicKey);
|
|
20
20
|
}
|
|
21
|
-
/** True iff `keypair`'s two fields are the exact byte lengths p384'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 function isValidClassicalKeypairShape(keypair) {
|
|
23
|
-
return keypair.secretKey.length === p384.lengths.secretKey && keypair.publicKey.length === p384.lengths.publicKey;
|
|
24
|
-
}
|
|
25
21
|
//# sourceMappingURL=classical.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classical.js","sourceRoot":"","sources":["../../src/crypto/classical.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAO7C,yEAAyE;AACzE,MAAM,UAAU,wBAAwB;IACtC,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAmB,EAAE,SAAqB;IACtE,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,SAAqB,EAAE,OAAmB,EAAE,SAAqB;IAC/F,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,qBAAqB,CAAC,SAAqB,EAAE,cAA0B;IACrF,OAAO,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;AACzD,CAAC
|
|
1
|
+
{"version":3,"file":"classical.js","sourceRoot":"","sources":["../../src/crypto/classical.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAO7C,yEAAyE;AACzE,MAAM,UAAU,wBAAwB;IACtC,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAmB,EAAE,SAAqB;IACtE,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,SAAqB,EAAE,OAAmB,EAAE,SAAqB;IAC/F,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,qBAAqB,CAAC,SAAqB,EAAE,cAA0B;IACrF,OAAO,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { gcm } from "@noble/ciphers/aes.js";
|
|
2
2
|
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
|
|
3
|
-
// Prefer FrameSender/FrameReceiver (./frameSession.ts), which own the counter so
|
|
4
|
-
// it cannot be reused by mistake; use these raw functions only if you must manage
|
|
5
|
-
// counters yourself.
|
|
6
|
-
//
|
|
7
3
|
// The per-frame AEAD used to wrap every message sent under a session key
|
|
8
4
|
// established by establishSessionInitiatorCore/establishSessionResponderCore
|
|
9
5
|
// (see hybrid.ts) — one directional key, reused across many frames, so the
|
|
@@ -24,10 +20,8 @@ export class FrameDecryptionError extends Error {
|
|
|
24
20
|
}
|
|
25
21
|
/** Encodes `counter` as a 96-bit nonce: 4 zero bytes + an 8-byte big-endian counter (2^64 frames is far beyond any realistic session). */
|
|
26
22
|
function counterToNonce(counter) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (!Number.isSafeInteger(counter) || counter < 0) {
|
|
30
|
-
throw new Error(`Frame counter must be a non-negative safe integer (0 to 2^53-1), got ${counter}.`);
|
|
23
|
+
if (!Number.isInteger(counter) || counter < 0) {
|
|
24
|
+
throw new Error(`Frame counter must be a non-negative integer, got ${counter}.`);
|
|
31
25
|
}
|
|
32
26
|
const nonce = new Uint8Array(NONCE_LENGTH);
|
|
33
27
|
let value = BigInt(counter);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frameCipher.js","sourceRoot":"","sources":["../../src/crypto/frameCipher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEhE,
|
|
1
|
+
{"version":3,"file":"frameCipher.js","sourceRoot":"","sources":["../../src/crypto/frameCipher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEhE,yEAAyE;AACzE,6EAA6E;AAC7E,2EAA2E;AAC3E,4EAA4E;AAC5E,yEAAyE;AACzE,4EAA4E;AAC5E,4EAA4E;AAC5E,4EAA4E;AAC5E,wEAAwE;AACxE,gBAAgB;AAEhB,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,aAAa,GAAG,CAAC,CAAC;AAExB,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C;QACE,KAAK,CAAC,+FAA+F,CAAC,CAAC;QACvG,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AASD,0IAA0I;AAC1I,SAAS,cAAc,CAAC,OAAe;IACrC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,qDAAqD,OAAO,GAAG,CAAC,CAAC;IACnF,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC;IAC3C,IAAI,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,YAAY,GAAG,CAAC,EAAE,CAAC,IAAI,YAAY,GAAG,aAAa,EAAE,CAAC,EAAE,EAAE,CAAC;QACtE,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;QACjC,KAAK,KAAK,EAAE,CAAC;IACf,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,YAAY,CAAC,GAAe,EAAE,OAAe,EAAE,SAAqB,EAAE,GAAgB;IACpG,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7E,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;AACzD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,GAAe,EAAE,KAAqB,EAAE,GAAgB;IACnF,IAAI,CAAC;QACH,OAAO,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAC5F,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,oBAAoB,EAAE,CAAC;IACnC,CAAC;AACH,CAAC"}
|
package/dist/crypto/hybrid.d.ts
CHANGED
|
@@ -7,23 +7,6 @@ declare const ALGORITHMS: {
|
|
|
7
7
|
export declare class UntrustedPartyError extends Error {
|
|
8
8
|
constructor(alias: string);
|
|
9
9
|
}
|
|
10
|
-
/** Thrown by decryptFromCore for a structurally-malformed EncryptedBox or a failed AEAD decryption — both mean "this box is unusable", so one error class covers both causes. */
|
|
11
|
-
export declare class DecryptionError extends Error {
|
|
12
|
-
constructor(message: string);
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* True only for the exact canonical format `new Date().toISOString()` itself
|
|
16
|
-
* produces — the one format this codebase ever actually writes. A round-trip
|
|
17
|
-
* check (not a regex, not bare `Date.parse`) catches wrong formats, non-ISO
|
|
18
|
-
* strings `Date.parse` would otherwise accept (implementation-defined beyond
|
|
19
|
-
* strict ISO-8601), AND calendar-overflow values some engines silently roll
|
|
20
|
-
* forward (e.g. a Feb 30th) — all in one comparison: any mismatch, whatever
|
|
21
|
-
* the cause, fails the round-trip (ADR 0010). Exported so other modules that
|
|
22
|
-
* define their own timestamp fields (e.g. trust.ts's rotation/revocation
|
|
23
|
-
* documents) can hold them to the same strict standard instead of each
|
|
24
|
-
* re-deriving their own looser check.
|
|
25
|
-
*/
|
|
26
|
-
export declare function isValidIsoInstant(value: string): boolean;
|
|
27
10
|
export interface Certificate {
|
|
28
11
|
documentHash: string;
|
|
29
12
|
classicalSignature: string;
|
|
@@ -35,14 +18,6 @@ export interface Certificate {
|
|
|
35
18
|
pqcSignPublicKey: string;
|
|
36
19
|
};
|
|
37
20
|
signedAt: string;
|
|
38
|
-
/**
|
|
39
|
-
* Certificate format. 2 (what sign() writes) means the signatures also cover
|
|
40
|
-
* `signer.alias` and `signedAt`; absent means a legacy certificate whose
|
|
41
|
-
* signatures cover only the document hash and expiry, so those two fields
|
|
42
|
-
* are unauthenticated there. A v2 certificate stripped of this field does
|
|
43
|
-
* not become a valid legacy one: its signatures are over the v2 message.
|
|
44
|
-
*/
|
|
45
|
-
version?: 2;
|
|
46
21
|
/** Absent = permanent proof, valid indefinitely (the default, unchanged behavior). */
|
|
47
22
|
expiresAt?: string;
|
|
48
23
|
}
|
|
@@ -50,7 +25,7 @@ export interface SignOptions {
|
|
|
50
25
|
/** If set, verify() rejects this certificate as "expired" once this ISO timestamp has passed. */
|
|
51
26
|
expiresAt?: string;
|
|
52
27
|
}
|
|
53
|
-
export type VerifyFailureReason = "untrusted-signer" | "revoked-signer" | "hash-mismatch" | "classical-signature-mismatch" | "pqc-signature-mismatch" | "expired"
|
|
28
|
+
export type VerifyFailureReason = "untrusted-signer" | "revoked-signer" | "hash-mismatch" | "classical-signature-mismatch" | "pqc-signature-mismatch" | "expired";
|
|
54
29
|
export type VerifyResult = {
|
|
55
30
|
valid: true;
|
|
56
31
|
} | {
|
|
@@ -93,57 +68,24 @@ export declare function signCore(identity: Identity, document: unknown, options?
|
|
|
93
68
|
*/
|
|
94
69
|
export declare function verifyCore(identity: Identity, document: unknown, certificate: Certificate, deps: VerifyDeps): Promise<VerifyResult>;
|
|
95
70
|
export interface EncryptedBox {
|
|
96
|
-
/** Always 2. Version 1 boxes (1.x, unsigned, no context binding) are rejected. */
|
|
97
|
-
version: 2;
|
|
98
|
-
/** 16 random bytes, hex. Unique per box; use it to detect replays. */
|
|
99
|
-
id: string;
|
|
100
|
-
/** When the sender sealed the box (ISO-8601, covered by the signature). */
|
|
101
|
-
signedAt: string;
|
|
102
71
|
kemCipherText: string;
|
|
103
72
|
nonce: string;
|
|
104
73
|
ciphertext: string;
|
|
105
|
-
/** The sender's dual (ECDSA-P384 + ML-DSA-87) signature over everything above and both party fingerprints. */
|
|
106
|
-
signature: {
|
|
107
|
-
classicalSignature: string;
|
|
108
|
-
pqcSignature: string;
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
/** Thrown by decryptFromCore when the caller's `isReplay` hook reports the (otherwise valid) box was already seen. */
|
|
112
|
-
export declare class ReplayedBoxError extends Error {
|
|
113
|
-
constructor(id: string);
|
|
114
|
-
}
|
|
115
|
-
/** What the replay hook is told about a box that has already passed signature verification. */
|
|
116
|
-
export interface BoxReplayInfo {
|
|
117
|
-
id: string;
|
|
118
|
-
signedAt: string;
|
|
119
|
-
senderFingerprint: string;
|
|
120
|
-
}
|
|
121
|
-
/** decryptFromCore's dependencies: trust, log, and an optional replay check (return true to reject the box). */
|
|
122
|
-
export interface DecryptDeps extends TrustDeps {
|
|
123
|
-
isReplay?: (info: BoxReplayInfo) => boolean | Promise<boolean>;
|
|
124
74
|
}
|
|
125
75
|
/**
|
|
126
|
-
* Encrypts `data` for `counterpart`
|
|
127
|
-
* ECDH-P384 and an ML-KEM-1024 encapsulation
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* it cannot be forged by the recipient or anyone else. Throws
|
|
131
|
-
* UntrustedPartyError unless `deps.checkTrust(counterpart)` reports "trusted".
|
|
132
|
-
*
|
|
133
|
-
* There is still NO forward secrecy: the ECDH half uses long-term keys. Use a
|
|
134
|
-
* session handshake for anything that needs it.
|
|
76
|
+
* Encrypts `data` for `counterpart` using a symmetric key derived from BOTH
|
|
77
|
+
* an ECDH-P384 exchange and an ML-KEM-1024 encapsulation (HKDF-SHA384
|
|
78
|
+
* combines the two secrets), then AES-256-GCM. Throws UntrustedPartyError
|
|
79
|
+
* unless `deps.checkTrust(counterpart)` reports "trusted".
|
|
135
80
|
*/
|
|
136
81
|
export declare function encryptForCore(identity: Identity, counterpart: PublicCard, data: unknown, deps: TrustDeps): Promise<EncryptedBox>;
|
|
137
82
|
/**
|
|
138
83
|
* Decrypts a box produced by `encryptFor`. `counterpart` is the sender's
|
|
139
84
|
* public card. Throws UntrustedPartyError unless `deps.checkTrust(counterpart)`
|
|
140
|
-
* reports "trusted"
|
|
141
|
-
*
|
|
142
|
-
* someone else, or a box not signed by `counterpart` is a DecryptionError. If
|
|
143
|
-
* `deps.isReplay` is given it is consulted only after the signature checks
|
|
144
|
-
* out, and a true result throws ReplayedBoxError.
|
|
85
|
+
* reports "trusted" — this stops a caller from accidentally decrypting "as
|
|
86
|
+
* if" a message came from someone they never actually trusted.
|
|
145
87
|
*/
|
|
146
|
-
export declare function decryptFromCore<T = unknown>(identity: Identity, counterpart: PublicCard, box: EncryptedBox, deps:
|
|
88
|
+
export declare function decryptFromCore<T = unknown>(identity: Identity, counterpart: PublicCard, box: EncryptedBox, deps: TrustDeps): Promise<T>;
|
|
147
89
|
export interface SessionKeys {
|
|
148
90
|
/** AES-256-GCM key for frames sent by the initiator, decrypted by the responder. */
|
|
149
91
|
initiatorToResponder: Uint8Array;
|
|
@@ -212,35 +154,6 @@ export interface ResponderSessionResult {
|
|
|
212
154
|
export declare class HandshakeVerificationError extends Error {
|
|
213
155
|
constructor(message: string);
|
|
214
156
|
}
|
|
215
|
-
/** Thrown for a structurally-malformed handshake message (non-hex fields) — distinct from HandshakeVerificationError, which means the shape was fine but the signature didn't check out. */
|
|
216
|
-
export declare class MalformedHandshakeMessageError extends Error {
|
|
217
|
-
constructor(message: string);
|
|
218
|
-
}
|
|
219
|
-
/** The four handshake artifacts a confirmation tag is computed over — exactly what both sides hold once the handshake completes. */
|
|
220
|
-
export interface HandshakeTranscript {
|
|
221
|
-
initiatorCard: PublicCard;
|
|
222
|
-
responderCard: PublicCard;
|
|
223
|
-
responderMessage: ResponderHandshakeMessage;
|
|
224
|
-
initiatorMessage: InitiatorHandshakeMessage;
|
|
225
|
-
}
|
|
226
|
-
export type HandshakeRole = "initiator" | "responder";
|
|
227
|
-
/**
|
|
228
|
-
* The tag to send the counterpart as proof you derived the same secrets:
|
|
229
|
-
* HMAC-SHA384, keyed by the handshake's `confirmationKey`, over the full
|
|
230
|
-
* transcript and YOUR role (hex, 96 characters). Folding the role in means
|
|
231
|
-
* your own tag can never be replayed back to you as the counterpart's.
|
|
232
|
-
* Send it only after the handshake call returned, and treat the connection
|
|
233
|
-
* as unauthenticated until `verifyKeyConfirmation` succeeds on the tag you
|
|
234
|
-
* receive.
|
|
235
|
-
*/
|
|
236
|
-
export declare function makeKeyConfirmation(confirmationKey: Uint8Array, role: HandshakeRole, transcript: HandshakeTranscript): string;
|
|
237
|
-
/**
|
|
238
|
-
* Checks the tag received from the counterpart. Pass the COUNTERPART's role
|
|
239
|
-
* (an initiator verifies "responder"). Returns false — never throws — for a
|
|
240
|
-
* wrong tag, a malformed one (any non-string, odd length, non-hex, wrong
|
|
241
|
-
* length, uppercase) or an unusable key/role, and compares in constant time.
|
|
242
|
-
*/
|
|
243
|
-
export declare function verifyKeyConfirmation(confirmationKey: Uint8Array, counterpartRole: HandshakeRole, transcript: HandshakeTranscript, tag: unknown): boolean;
|
|
244
157
|
/**
|
|
245
158
|
* First step of the handshake, run by the connection's responder: generates
|
|
246
159
|
* one fresh, single-use ECDH-P384 keypair and one fresh, single-use
|