@sebastienrousseau/crypto-lib 0.0.2 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +334 -185
- package/dist/accel/index.d.ts +11 -0
- package/dist/accel/index.d.ts.map +1 -0
- package/dist/accel/index.js +31 -0
- package/dist/accel/native-pqc-bridge.d.ts +18 -0
- package/dist/accel/native-pqc-bridge.d.ts.map +1 -0
- package/dist/accel/native-pqc-bridge.js +100 -0
- package/dist/accel/wasm-bridge.d.ts +35 -0
- package/dist/accel/wasm-bridge.d.ts.map +1 -0
- package/dist/accel/wasm-bridge.js +161 -0
- package/dist/accel/webcrypto-bridge.d.ts +32 -0
- package/dist/accel/webcrypto-bridge.d.ts.map +1 -0
- package/dist/accel/webcrypto-bridge.js +179 -0
- package/dist/accel/webcrypto-modern.d.ts +40 -0
- package/dist/accel/webcrypto-modern.d.ts.map +1 -0
- package/dist/accel/webcrypto-modern.js +213 -0
- package/dist/accel/worker-pool.d.ts +23 -0
- package/dist/accel/worker-pool.d.ts.map +1 -0
- package/dist/accel/worker-pool.js +119 -0
- package/dist/bin/cryptolib.d.ts +25 -0
- package/dist/bin/cryptolib.d.ts.map +1 -0
- package/dist/bin/cryptolib.js +87 -0
- package/dist/config/config.d.ts +8 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +13 -0
- package/dist/crypto.d.ts +43 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +107 -0
- package/dist/enums.d.ts +81 -0
- package/dist/enums.d.ts.map +1 -0
- package/dist/enums.js +82 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +22 -0
- package/dist/high-level/index.d.ts +11 -0
- package/dist/high-level/index.d.ts.map +1 -0
- package/dist/high-level/index.js +53 -0
- package/dist/high-level/key-wrap.d.ts +16 -0
- package/dist/high-level/key-wrap.d.ts.map +1 -0
- package/dist/high-level/key-wrap.js +93 -0
- package/dist/high-level/multi-recipient.d.ts +25 -0
- package/dist/high-level/multi-recipient.d.ts.map +1 -0
- package/dist/high-level/multi-recipient.js +122 -0
- package/dist/high-level/password-encrypt.d.ts +14 -0
- package/dist/high-level/password-encrypt.d.ts.map +1 -0
- package/dist/high-level/password-encrypt.js +93 -0
- package/dist/high-level/sealedbox.d.ts +13 -0
- package/dist/high-level/sealedbox.d.ts.map +1 -0
- package/dist/high-level/sealedbox.js +119 -0
- package/dist/high-level/secretbox.d.ts +7 -0
- package/dist/high-level/secretbox.d.ts.map +1 -0
- package/dist/high-level/secretbox.js +54 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -2
- package/dist/key/key.d.ts +10 -0
- package/dist/key/key.d.ts.map +1 -0
- package/dist/key/key.js +25 -0
- package/dist/key/keystore.d.ts +11 -0
- package/dist/key/keystore.d.ts.map +1 -0
- package/dist/key/keystore.js +87 -0
- package/dist/keys/index.d.ts +7 -0
- package/dist/keys/index.d.ts.map +1 -0
- package/dist/keys/index.js +22 -0
- package/dist/keys/keygen.d.ts +16 -0
- package/dist/keys/keygen.d.ts.map +1 -0
- package/dist/keys/keygen.js +128 -0
- package/dist/keys/keyring.d.ts +35 -0
- package/dist/keys/keyring.d.ts.map +1 -0
- package/dist/keys/keyring.js +136 -0
- package/dist/keys/serialize.d.ts +30 -0
- package/dist/keys/serialize.d.ts.map +1 -0
- package/dist/keys/serialize.js +120 -0
- package/dist/lib/decrypt.d.ts +5 -5
- package/dist/lib/decrypt.d.ts.map +1 -1
- package/dist/lib/decrypt.js +49 -39
- package/dist/lib/encrypt.d.ts +2 -5
- package/dist/lib/encrypt.d.ts.map +1 -1
- package/dist/lib/encrypt.js +34 -39
- package/dist/lib/generate.d.ts +2 -11
- package/dist/lib/generate.d.ts.map +1 -1
- package/dist/lib/generate.js +57 -29
- package/dist/lib/index.d.ts +10 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +53 -0
- package/dist/lib/key-output.d.ts +7 -0
- package/dist/lib/key-output.d.ts.map +1 -0
- package/dist/lib/key-output.js +50 -0
- package/dist/lib/reformat.d.ts +9 -0
- package/dist/lib/reformat.d.ts.map +1 -0
- package/dist/lib/reformat.js +67 -0
- package/dist/lib/revoke.d.ts +3 -3
- package/dist/lib/revoke.d.ts.map +1 -1
- package/dist/lib/revoke.js +36 -28
- package/dist/lib/session.d.ts +5 -0
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/session.js +55 -0
- package/dist/lib/sign.d.ts +4 -0
- package/dist/lib/sign.d.ts.map +1 -0
- package/dist/lib/sign.js +63 -0
- package/dist/lib/verify.d.ts +5 -0
- package/dist/lib/verify.d.ts.map +1 -0
- package/dist/lib/verify.js +52 -0
- package/dist/modern/aead.d.ts +17 -0
- package/dist/modern/aead.d.ts.map +1 -0
- package/dist/modern/aead.js +51 -0
- package/dist/modern/aes.d.ts +35 -0
- package/dist/modern/aes.d.ts.map +1 -0
- package/dist/modern/aes.js +82 -0
- package/dist/modern/cost-limits.d.ts +11 -0
- package/dist/modern/cost-limits.d.ts.map +1 -0
- package/dist/modern/cost-limits.js +35 -0
- package/dist/modern/curves.d.ts +86 -0
- package/dist/modern/curves.d.ts.map +1 -0
- package/dist/modern/curves.js +171 -0
- package/dist/modern/ecdh.d.ts +11 -0
- package/dist/modern/ecdh.d.ts.map +1 -0
- package/dist/modern/ecdh.js +28 -0
- package/dist/modern/fn-dsa.d.ts +19 -0
- package/dist/modern/fn-dsa.d.ts.map +1 -0
- package/dist/modern/fn-dsa.js +57 -0
- package/dist/modern/hash.d.ts +13 -0
- package/dist/modern/hash.d.ts.map +1 -0
- package/dist/modern/hash.js +45 -0
- package/dist/modern/hpke.d.ts +47 -0
- package/dist/modern/hpke.d.ts.map +1 -0
- package/dist/modern/hpke.js +207 -0
- package/dist/modern/index.d.ts +40 -0
- package/dist/modern/index.d.ts.map +1 -0
- package/dist/modern/index.js +194 -0
- package/dist/modern/kdf.d.ts +23 -0
- package/dist/modern/kdf.d.ts.map +1 -0
- package/dist/modern/kdf.js +67 -0
- package/dist/modern/mac.d.ts +51 -0
- package/dist/modern/mac.d.ts.map +1 -0
- package/dist/modern/mac.js +119 -0
- package/dist/modern/password.d.ts +40 -0
- package/dist/modern/password.d.ts.map +1 -0
- package/dist/modern/password.js +123 -0
- package/dist/modern/pq-hash-sign.d.ts +18 -0
- package/dist/modern/pq-hash-sign.d.ts.map +1 -0
- package/dist/modern/pq-hash-sign.js +73 -0
- package/dist/modern/pq-kem.d.ts +88 -0
- package/dist/modern/pq-kem.d.ts.map +1 -0
- package/dist/modern/pq-kem.js +270 -0
- package/dist/modern/pq-sign.d.ts +30 -0
- package/dist/modern/pq-sign.d.ts.map +1 -0
- package/dist/modern/pq-sign.js +94 -0
- package/dist/modern/pq.d.ts +38 -0
- package/dist/modern/pq.d.ts.map +1 -0
- package/dist/modern/pq.js +80 -0
- package/dist/modern/signing.d.ts +16 -0
- package/dist/modern/signing.d.ts.map +1 -0
- package/dist/modern/signing.js +34 -0
- package/dist/protocols/index.d.ts +7 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/index.js +46 -0
- package/dist/protocols/iso20022.d.ts +51 -0
- package/dist/protocols/iso20022.d.ts.map +1 -0
- package/dist/protocols/iso20022.js +145 -0
- package/dist/protocols/opaque/ake.d.ts +51 -0
- package/dist/protocols/opaque/ake.d.ts.map +1 -0
- package/dist/protocols/opaque/ake.js +102 -0
- package/dist/protocols/opaque/client.d.ts +45 -0
- package/dist/protocols/opaque/client.d.ts.map +1 -0
- package/dist/protocols/opaque/client.js +63 -0
- package/dist/protocols/opaque/config.d.ts +14 -0
- package/dist/protocols/opaque/config.d.ts.map +1 -0
- package/dist/protocols/opaque/config.js +18 -0
- package/dist/protocols/opaque/credentials.d.ts +29 -0
- package/dist/protocols/opaque/credentials.d.ts.map +1 -0
- package/dist/protocols/opaque/credentials.js +61 -0
- package/dist/protocols/opaque/envelope.d.ts +36 -0
- package/dist/protocols/opaque/envelope.d.ts.map +1 -0
- package/dist/protocols/opaque/envelope.js +77 -0
- package/dist/protocols/opaque/ksf.d.ts +15 -0
- package/dist/protocols/opaque/ksf.d.ts.map +1 -0
- package/dist/protocols/opaque/ksf.js +27 -0
- package/dist/protocols/opaque/messages.d.ts +61 -0
- package/dist/protocols/opaque/messages.d.ts.map +1 -0
- package/dist/protocols/opaque/messages.js +94 -0
- package/dist/protocols/opaque/server.d.ts +33 -0
- package/dist/protocols/opaque/server.d.ts.map +1 -0
- package/dist/protocols/opaque/server.js +74 -0
- package/dist/protocols/opaque/suite.d.ts +58 -0
- package/dist/protocols/opaque/suite.d.ts.map +1 -0
- package/dist/protocols/opaque/suite.js +115 -0
- package/dist/protocols/pake.d.ts +14 -0
- package/dist/protocols/pake.d.ts.map +1 -0
- package/dist/protocols/pake.js +33 -0
- package/dist/protocols/pqxdh.d.ts +53 -0
- package/dist/protocols/pqxdh.d.ts.map +1 -0
- package/dist/protocols/pqxdh.js +143 -0
- package/dist/protocols/ratchet.d.ts +52 -0
- package/dist/protocols/ratchet.d.ts.map +1 -0
- package/dist/protocols/ratchet.js +187 -0
- package/dist/protocols/threshold.d.ts +23 -0
- package/dist/protocols/threshold.d.ts.map +1 -0
- package/dist/protocols/threshold.js +191 -0
- package/dist/registry.d.ts +20 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +399 -0
- package/dist/streaming/index.d.ts +7 -0
- package/dist/streaming/index.d.ts.map +1 -0
- package/dist/streaming/index.js +15 -0
- package/dist/streaming/stream-aead.d.ts +17 -0
- package/dist/streaming/stream-aead.d.ts.map +1 -0
- package/dist/streaming/stream-aead.js +125 -0
- package/dist/streaming/stream-hash.d.ts +9 -0
- package/dist/streaming/stream-hash.d.ts.map +1 -0
- package/dist/streaming/stream-hash.js +52 -0
- package/dist/streaming/web-streams.d.ts +17 -0
- package/dist/streaming/web-streams.d.ts.map +1 -0
- package/dist/streaming/web-streams.js +114 -0
- package/dist/tokens/index.d.ts +5 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/index.js +15 -0
- package/dist/tokens/key-rotation.d.ts +20 -0
- package/dist/tokens/key-rotation.d.ts.map +1 -0
- package/dist/tokens/key-rotation.js +76 -0
- package/dist/tokens/paseto.d.ts +46 -0
- package/dist/tokens/paseto.d.ts.map +1 -0
- package/dist/tokens/paseto.js +200 -0
- package/dist/types/types.d.ts +57 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +1 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +56 -0
- package/package.json +77 -130
- package/dist/COPYRIGHT +0 -15
- package/dist/Report.txt +0 -72
- package/dist/bin/crypto-lib.d.ts +0 -13
- package/dist/bin/crypto-lib.d.ts.map +0 -1
- package/dist/bin/crypto-lib.js +0 -52
- package/dist/bin/crypto-lib.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/decrypt.js.map +0 -1
- package/dist/lib/encrypt.js.map +0 -1
- package/dist/lib/generate.js.map +0 -1
- package/dist/lib/revoke.js.map +0 -1
- package/dist/package.json +0 -131
- package/dist/types/types.js.map +0 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface IdentityKeyPair {
|
|
2
|
+
privateKey: string;
|
|
3
|
+
publicKey: string;
|
|
4
|
+
signingPrivateKey: string;
|
|
5
|
+
signingPublicKey: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SignedPreKey {
|
|
8
|
+
privateKey: string;
|
|
9
|
+
publicKey: string;
|
|
10
|
+
signature: string;
|
|
11
|
+
}
|
|
12
|
+
export interface OneTimePreKey {
|
|
13
|
+
privateKey: string;
|
|
14
|
+
publicKey: string;
|
|
15
|
+
}
|
|
16
|
+
export interface PqPreKey {
|
|
17
|
+
publicKey: string;
|
|
18
|
+
secretKey: string;
|
|
19
|
+
signature: string;
|
|
20
|
+
}
|
|
21
|
+
export interface InitiateSessionParams {
|
|
22
|
+
identityKeyPair: IdentityKeyPair;
|
|
23
|
+
remoteIdentityPublic: string;
|
|
24
|
+
remoteSignedPreKeyPublic: string;
|
|
25
|
+
remoteOneTimePreKeyPublic?: string;
|
|
26
|
+
remotePqPreKeyPublic: string;
|
|
27
|
+
}
|
|
28
|
+
export interface InitiateSessionResult {
|
|
29
|
+
sharedSecret: string;
|
|
30
|
+
ephemeralPublicKey: string;
|
|
31
|
+
pqCiphertext: string;
|
|
32
|
+
algorithm: "pqxdh";
|
|
33
|
+
}
|
|
34
|
+
export interface RespondToSessionParams {
|
|
35
|
+
identityKeyPair: IdentityKeyPair;
|
|
36
|
+
signedPreKeyPrivate: string;
|
|
37
|
+
oneTimePreKeyPrivate?: string;
|
|
38
|
+
pqPreKeySecret: string;
|
|
39
|
+
remoteIdentityPublic: string;
|
|
40
|
+
remoteEphemeralPublic: string;
|
|
41
|
+
pqCiphertext: string;
|
|
42
|
+
}
|
|
43
|
+
export interface RespondToSessionResult {
|
|
44
|
+
sharedSecret: string;
|
|
45
|
+
algorithm: "pqxdh";
|
|
46
|
+
}
|
|
47
|
+
export declare function generateIdentityKeyPair(): IdentityKeyPair;
|
|
48
|
+
export declare function generateSignedPreKey(identitySigningPrivate: string): SignedPreKey;
|
|
49
|
+
export declare function generateOneTimePreKey(): OneTimePreKey;
|
|
50
|
+
export declare function generatePqPreKey(identitySigningPrivate: string): PqPreKey;
|
|
51
|
+
export declare function initiateSession(params: InitiateSessionParams): InitiateSessionResult;
|
|
52
|
+
export declare function respondToSession(params: RespondToSessionParams): RespondToSessionResult;
|
|
53
|
+
//# sourceMappingURL=pqxdh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pqxdh.d.ts","sourceRoot":"","sources":["../../src/protocols/pqxdh.ts"],"names":[],"mappings":"AA8BA,MAAM,WAAW,eAAe;IAE9B,UAAU,EAAE,MAAM,CAAC;IAEnB,SAAS,EAAE,MAAM,CAAC;IAElB,iBAAiB,EAAE,MAAM,CAAC;IAE1B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAGD,MAAM,WAAW,YAAY;IAE3B,UAAU,EAAE,MAAM,CAAC;IAEnB,SAAS,EAAE,MAAM,CAAC;IAElB,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,aAAa;IAE5B,UAAU,EAAE,MAAM,CAAC;IAEnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,QAAQ;IAEvB,SAAS,EAAE,MAAM,CAAC;IAElB,SAAS,EAAE,MAAM,CAAC;IAElB,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,qBAAqB;IAEpC,eAAe,EAAE,eAAe,CAAC;IAEjC,oBAAoB,EAAE,MAAM,CAAC;IAE7B,wBAAwB,EAAE,MAAM,CAAC;IAEjC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAEnC,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAGD,MAAM,WAAW,qBAAqB;IAEpC,YAAY,EAAE,MAAM,CAAC;IAErB,kBAAkB,EAAE,MAAM,CAAC;IAE3B,YAAY,EAAE,MAAM,CAAC;IAErB,SAAS,EAAE,OAAO,CAAC;CACpB;AAGD,MAAM,WAAW,sBAAsB;IAErC,eAAe,EAAE,eAAe,CAAC;IAEjC,mBAAmB,EAAE,MAAM,CAAC;IAE5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B,cAAc,EAAE,MAAM,CAAC;IAEvB,oBAAoB,EAAE,MAAM,CAAC;IAE7B,qBAAqB,EAAE,MAAM,CAAC;IAE9B,YAAY,EAAE,MAAM,CAAC;CACtB;AAGD,MAAM,WAAW,sBAAsB;IAErC,YAAY,EAAE,MAAM,CAAC;IAErB,SAAS,EAAE,OAAO,CAAC;CACpB;AA0BD,wBAAgB,uBAAuB,IAAI,eAAe,CAYzD;AAQD,wBAAgB,oBAAoB,CAClC,sBAAsB,EAAE,MAAM,GAC7B,YAAY,CAWd;AAKD,wBAAgB,qBAAqB,IAAI,aAAa,CAQrD;AAKD,wBAAgB,gBAAgB,CAAC,sBAAsB,EAAE,MAAM,GAAG,QAAQ,CAezE;AAUD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,qBAAqB,GAC5B,qBAAqB,CAqEvB;AAQD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,sBAAsB,GAC7B,sBAAsB,CA+DxB"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateIdentityKeyPair = generateIdentityKeyPair;
|
|
4
|
+
exports.generateSignedPreKey = generateSignedPreKey;
|
|
5
|
+
exports.generateOneTimePreKey = generateOneTimePreKey;
|
|
6
|
+
exports.generatePqPreKey = generatePqPreKey;
|
|
7
|
+
exports.initiateSession = initiateSession;
|
|
8
|
+
exports.respondToSession = respondToSession;
|
|
9
|
+
const ed25519_js_1 = require("@noble/curves/ed25519.js");
|
|
10
|
+
const ml_kem_js_1 = require("@noble/post-quantum/ml-kem.js");
|
|
11
|
+
const hkdf_js_1 = require("@noble/hashes/hkdf.js");
|
|
12
|
+
const sha2_js_1 = require("@noble/hashes/sha2.js");
|
|
13
|
+
const utils_js_1 = require("@noble/ciphers/utils.js");
|
|
14
|
+
const HEX_RE = /^[0-9a-fA-F]*$/;
|
|
15
|
+
function hexToBytes(hex, label) {
|
|
16
|
+
if (!HEX_RE.test(hex)) {
|
|
17
|
+
throw new Error(`Invalid hex string for ${label}`);
|
|
18
|
+
}
|
|
19
|
+
return Buffer.from(hex, "hex");
|
|
20
|
+
}
|
|
21
|
+
const PQXDH_INFO = new TextEncoder().encode("pqxdh-v1");
|
|
22
|
+
function generateIdentityKeyPair() {
|
|
23
|
+
const x25519Priv = (0, utils_js_1.randomBytes)(32);
|
|
24
|
+
const x25519Pub = ed25519_js_1.x25519.getPublicKey(x25519Priv);
|
|
25
|
+
const signingPriv = (0, utils_js_1.randomBytes)(32);
|
|
26
|
+
const signingPub = ed25519_js_1.ed25519.getPublicKey(signingPriv);
|
|
27
|
+
return {
|
|
28
|
+
privateKey: Buffer.from(x25519Priv).toString("hex"),
|
|
29
|
+
publicKey: Buffer.from(x25519Pub).toString("hex"),
|
|
30
|
+
signingPrivateKey: Buffer.from(signingPriv).toString("hex"),
|
|
31
|
+
signingPublicKey: Buffer.from(signingPub).toString("hex"),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function generateSignedPreKey(identitySigningPrivate) {
|
|
35
|
+
const sigKey = hexToBytes(identitySigningPrivate, "identitySigningPrivate");
|
|
36
|
+
const prePriv = (0, utils_js_1.randomBytes)(32);
|
|
37
|
+
const prePub = ed25519_js_1.x25519.getPublicKey(prePriv);
|
|
38
|
+
const signature = ed25519_js_1.ed25519.sign(prePub, sigKey);
|
|
39
|
+
return {
|
|
40
|
+
privateKey: Buffer.from(prePriv).toString("hex"),
|
|
41
|
+
publicKey: Buffer.from(prePub).toString("hex"),
|
|
42
|
+
signature: Buffer.from(signature).toString("hex"),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function generateOneTimePreKey() {
|
|
46
|
+
const priv = (0, utils_js_1.randomBytes)(32);
|
|
47
|
+
const pub = ed25519_js_1.x25519.getPublicKey(priv);
|
|
48
|
+
return {
|
|
49
|
+
privateKey: Buffer.from(priv).toString("hex"),
|
|
50
|
+
publicKey: Buffer.from(pub).toString("hex"),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function generatePqPreKey(identitySigningPrivate) {
|
|
54
|
+
const sigKey = hexToBytes(identitySigningPrivate, "identitySigningPrivate");
|
|
55
|
+
const { publicKey, secretKey } = ml_kem_js_1.ml_kem768.keygen();
|
|
56
|
+
const signature = ed25519_js_1.ed25519.sign((0, sha2_js_1.sha256)(publicKey), sigKey);
|
|
57
|
+
return {
|
|
58
|
+
publicKey: Buffer.from(publicKey).toString("hex"),
|
|
59
|
+
secretKey: Buffer.from(secretKey).toString("hex"),
|
|
60
|
+
signature: Buffer.from(signature).toString("hex"),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function initiateSession(params) {
|
|
64
|
+
const { identityKeyPair, remoteIdentityPublic, remoteSignedPreKeyPublic, remoteOneTimePreKeyPublic, remotePqPreKeyPublic, } = params;
|
|
65
|
+
const ikPriv = hexToBytes(identityKeyPair.privateKey, "identityPrivateKey");
|
|
66
|
+
const remoteIkPub = hexToBytes(remoteIdentityPublic, "remoteIdentityPublic");
|
|
67
|
+
const remoteSPKPub = hexToBytes(remoteSignedPreKeyPublic, "remoteSignedPreKeyPublic");
|
|
68
|
+
const remotePqPub = hexToBytes(remotePqPreKeyPublic, "remotePqPreKeyPublic");
|
|
69
|
+
const ephPriv = (0, utils_js_1.randomBytes)(32);
|
|
70
|
+
const ephPub = ed25519_js_1.x25519.getPublicKey(ephPriv);
|
|
71
|
+
const dh1 = ed25519_js_1.x25519.getSharedSecret(ikPriv, remoteSPKPub);
|
|
72
|
+
const dh2 = ed25519_js_1.x25519.getSharedSecret(ephPriv, remoteIkPub);
|
|
73
|
+
const dh3 = ed25519_js_1.x25519.getSharedSecret(ephPriv, remoteSPKPub);
|
|
74
|
+
let dh4 = null;
|
|
75
|
+
if (remoteOneTimePreKeyPublic) {
|
|
76
|
+
const remoteOPKPub = hexToBytes(remoteOneTimePreKeyPublic, "remoteOneTimePreKeyPublic");
|
|
77
|
+
dh4 = ed25519_js_1.x25519.getSharedSecret(ephPriv, remoteOPKPub);
|
|
78
|
+
}
|
|
79
|
+
const { cipherText: pqCt, sharedSecret: pqShared } = ml_kem_js_1.ml_kem768.encapsulate(remotePqPub);
|
|
80
|
+
const dhLen = dh1.length + dh2.length + dh3.length;
|
|
81
|
+
const dh4Len = dh4 ? dh4.length : 0;
|
|
82
|
+
const totalLen = dhLen + dh4Len + pqShared.length;
|
|
83
|
+
const combined = new Uint8Array(totalLen);
|
|
84
|
+
let offset = 0;
|
|
85
|
+
combined.set(dh1, offset);
|
|
86
|
+
offset += dh1.length;
|
|
87
|
+
combined.set(dh2, offset);
|
|
88
|
+
offset += dh2.length;
|
|
89
|
+
combined.set(dh3, offset);
|
|
90
|
+
offset += dh3.length;
|
|
91
|
+
if (dh4) {
|
|
92
|
+
combined.set(dh4, offset);
|
|
93
|
+
offset += dh4.length;
|
|
94
|
+
}
|
|
95
|
+
combined.set(pqShared, offset);
|
|
96
|
+
const sharedSecret = (0, hkdf_js_1.hkdf)(sha2_js_1.sha256, combined, undefined, PQXDH_INFO, 32);
|
|
97
|
+
return {
|
|
98
|
+
sharedSecret: Buffer.from(sharedSecret).toString("hex"),
|
|
99
|
+
ephemeralPublicKey: Buffer.from(ephPub).toString("hex"),
|
|
100
|
+
pqCiphertext: Buffer.from(pqCt).toString("hex"),
|
|
101
|
+
algorithm: "pqxdh",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function respondToSession(params) {
|
|
105
|
+
const { identityKeyPair, signedPreKeyPrivate, oneTimePreKeyPrivate, pqPreKeySecret, remoteIdentityPublic, remoteEphemeralPublic, pqCiphertext, } = params;
|
|
106
|
+
const ikPriv = hexToBytes(identityKeyPair.privateKey, "identityPrivateKey");
|
|
107
|
+
const spkPriv = hexToBytes(signedPreKeyPrivate, "signedPreKeyPrivate");
|
|
108
|
+
const pqSecret = hexToBytes(pqPreKeySecret, "pqPreKeySecret");
|
|
109
|
+
const remoteIkPub = hexToBytes(remoteIdentityPublic, "remoteIdentityPublic");
|
|
110
|
+
const remoteEphPub = hexToBytes(remoteEphemeralPublic, "remoteEphemeralPublic");
|
|
111
|
+
const pqCt = hexToBytes(pqCiphertext, "pqCiphertext");
|
|
112
|
+
const dh1 = ed25519_js_1.x25519.getSharedSecret(spkPriv, remoteIkPub);
|
|
113
|
+
const dh2 = ed25519_js_1.x25519.getSharedSecret(ikPriv, remoteEphPub);
|
|
114
|
+
const dh3 = ed25519_js_1.x25519.getSharedSecret(spkPriv, remoteEphPub);
|
|
115
|
+
let dh4 = null;
|
|
116
|
+
if (oneTimePreKeyPrivate) {
|
|
117
|
+
const opkPriv = hexToBytes(oneTimePreKeyPrivate, "oneTimePreKeyPrivate");
|
|
118
|
+
dh4 = ed25519_js_1.x25519.getSharedSecret(opkPriv, remoteEphPub);
|
|
119
|
+
}
|
|
120
|
+
const pqShared = ml_kem_js_1.ml_kem768.decapsulate(pqCt, pqSecret);
|
|
121
|
+
const dhLen = dh1.length + dh2.length + dh3.length;
|
|
122
|
+
const dh4Len = dh4 ? dh4.length : 0;
|
|
123
|
+
const totalLen = dhLen + dh4Len + pqShared.length;
|
|
124
|
+
const combined = new Uint8Array(totalLen);
|
|
125
|
+
let offset = 0;
|
|
126
|
+
combined.set(dh1, offset);
|
|
127
|
+
offset += dh1.length;
|
|
128
|
+
combined.set(dh2, offset);
|
|
129
|
+
offset += dh2.length;
|
|
130
|
+
combined.set(dh3, offset);
|
|
131
|
+
offset += dh3.length;
|
|
132
|
+
if (dh4) {
|
|
133
|
+
combined.set(dh4, offset);
|
|
134
|
+
offset += dh4.length;
|
|
135
|
+
}
|
|
136
|
+
combined.set(pqShared, offset);
|
|
137
|
+
const sharedSecret = (0, hkdf_js_1.hkdf)(sha2_js_1.sha256, combined, undefined, PQXDH_INFO, 32);
|
|
138
|
+
return {
|
|
139
|
+
sharedSecret: Buffer.from(sharedSecret).toString("hex"),
|
|
140
|
+
algorithm: "pqxdh",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=pqxdh.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface MessageHeader {
|
|
2
|
+
publicKey: string;
|
|
3
|
+
previousChainLength: number;
|
|
4
|
+
messageNumber: number;
|
|
5
|
+
}
|
|
6
|
+
export interface EncryptedMessage {
|
|
7
|
+
header: MessageHeader;
|
|
8
|
+
ciphertext: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SymmetricRatchetState {
|
|
11
|
+
chainKey: string;
|
|
12
|
+
index: number;
|
|
13
|
+
}
|
|
14
|
+
export interface DoubleRatchetState {
|
|
15
|
+
sendingRatchetPrivate: string;
|
|
16
|
+
sendingRatchetPublic: string;
|
|
17
|
+
receivingRatchetPublic: string | null;
|
|
18
|
+
rootKey: string;
|
|
19
|
+
sendingChainKey: string | null;
|
|
20
|
+
receivingChainKey: string | null;
|
|
21
|
+
sendingMessageNumber: number;
|
|
22
|
+
receivingMessageNumber: number;
|
|
23
|
+
previousChainLength: number;
|
|
24
|
+
skippedKeys: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
export declare class SymmetricRatchet {
|
|
27
|
+
private _chainKey;
|
|
28
|
+
private _index;
|
|
29
|
+
constructor(chainKey: string | Uint8Array, index?: number);
|
|
30
|
+
get state(): SymmetricRatchetState;
|
|
31
|
+
next(): {
|
|
32
|
+
messageKey: Uint8Array;
|
|
33
|
+
index: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export declare class DoubleRatchet {
|
|
37
|
+
private _state;
|
|
38
|
+
private constructor();
|
|
39
|
+
get state(): DoubleRatchetState;
|
|
40
|
+
static initAlice(sharedSecret: string, remoteRatchetPublic: string): DoubleRatchet;
|
|
41
|
+
static initBob(sharedSecret: string, ourRatchetKeyPair: {
|
|
42
|
+
privateKey: string;
|
|
43
|
+
publicKey: string;
|
|
44
|
+
}): DoubleRatchet;
|
|
45
|
+
static fromState(state: DoubleRatchetState): DoubleRatchet;
|
|
46
|
+
encrypt(plaintext: string | Uint8Array): EncryptedMessage;
|
|
47
|
+
decrypt(header: MessageHeader, ciphertext: string): Uint8Array;
|
|
48
|
+
private _decryptWithKey;
|
|
49
|
+
private _dhRatchetStep;
|
|
50
|
+
private _skipMessages;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=ratchet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratchet.d.ts","sourceRoot":"","sources":["../../src/protocols/ratchet.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,aAAa;IAE5B,SAAS,EAAE,MAAM,CAAC;IAElB,mBAAmB,EAAE,MAAM,CAAC;IAE5B,aAAa,EAAE,MAAM,CAAC;CACvB;AAGD,MAAM,WAAW,gBAAgB;IAE/B,MAAM,EAAE,aAAa,CAAC;IAEtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAGD,MAAM,WAAW,qBAAqB;IAEpC,QAAQ,EAAE,MAAM,CAAC;IAEjB,KAAK,EAAE,MAAM,CAAC;CACf;AAGD,MAAM,WAAW,kBAAkB;IAEjC,qBAAqB,EAAE,MAAM,CAAC;IAE9B,oBAAoB,EAAE,MAAM,CAAC;IAE7B,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEtC,OAAO,EAAE,MAAM,CAAC;IAEhB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC,oBAAoB,EAAE,MAAM,CAAC;IAE7B,sBAAsB,EAAE,MAAM,CAAC;IAE/B,mBAAmB,EAAE,MAAM,CAAC;IAE5B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAkCD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,MAAM,CAAS;gBAGX,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,KAAK,SAAI;IAOpD,IAAI,KAAK,IAAI,qBAAqB,CAKjC;IAQD,IAAI,IAAI;QAEN,UAAU,EAAE,UAAU,CAAC;QAEvB,KAAK,EAAE,MAAM,CAAC;KACf;CASF;AAYD,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAqB;IAEnC,OAAO;IAKP,IAAI,KAAK,IAAI,kBAAkB,CAE9B;IAQD,MAAM,CAAC,SAAS,CACd,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,GAC1B,aAAa;IAwChB,MAAM,CAAC,OAAO,CACZ,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAC3D,aAAa;IAkBhB,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa;IAU1D,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,gBAAgB;IAgDzD,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU;IAwC9D,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,cAAc;IA4CtB,OAAO,CAAC,aAAa;CAiBtB"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DoubleRatchet = exports.SymmetricRatchet = void 0;
|
|
4
|
+
const ed25519_js_1 = require("@noble/curves/ed25519.js");
|
|
5
|
+
const hkdf_js_1 = require("@noble/hashes/hkdf.js");
|
|
6
|
+
const sha2_js_1 = require("@noble/hashes/sha2.js");
|
|
7
|
+
const hmac_js_1 = require("@noble/hashes/hmac.js");
|
|
8
|
+
const chacha_js_1 = require("@noble/ciphers/chacha.js");
|
|
9
|
+
const utils_js_1 = require("@noble/ciphers/utils.js");
|
|
10
|
+
const NONCE_LEN = 24;
|
|
11
|
+
const HEX_RE = /^[0-9a-fA-F]*$/;
|
|
12
|
+
const MAX_SKIP = 256;
|
|
13
|
+
function hexToBytes(hex) {
|
|
14
|
+
if (!HEX_RE.test(hex)) {
|
|
15
|
+
throw new Error("Invalid hex string");
|
|
16
|
+
}
|
|
17
|
+
return Buffer.from(hex, "hex");
|
|
18
|
+
}
|
|
19
|
+
function bytesToHex(bytes) {
|
|
20
|
+
return Buffer.from(bytes).toString("hex");
|
|
21
|
+
}
|
|
22
|
+
class SymmetricRatchet {
|
|
23
|
+
_chainKey;
|
|
24
|
+
_index;
|
|
25
|
+
constructor(chainKey, index = 0) {
|
|
26
|
+
this._chainKey =
|
|
27
|
+
typeof chainKey === "string" ? hexToBytes(chainKey) : chainKey;
|
|
28
|
+
this._index = index;
|
|
29
|
+
}
|
|
30
|
+
get state() {
|
|
31
|
+
return {
|
|
32
|
+
chainKey: bytesToHex(this._chainKey),
|
|
33
|
+
index: this._index,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
next() {
|
|
37
|
+
const messageKey = (0, hmac_js_1.hmac)(sha2_js_1.sha256, this._chainKey, new Uint8Array([0x01]));
|
|
38
|
+
this._chainKey = (0, hmac_js_1.hmac)(sha2_js_1.sha256, this._chainKey, new Uint8Array([0x02]));
|
|
39
|
+
const index = this._index;
|
|
40
|
+
this._index++;
|
|
41
|
+
return { messageKey, index };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.SymmetricRatchet = SymmetricRatchet;
|
|
45
|
+
class DoubleRatchet {
|
|
46
|
+
_state;
|
|
47
|
+
constructor(state) {
|
|
48
|
+
this._state = state;
|
|
49
|
+
}
|
|
50
|
+
get state() {
|
|
51
|
+
return { ...this._state };
|
|
52
|
+
}
|
|
53
|
+
static initAlice(sharedSecret, remoteRatchetPublic) {
|
|
54
|
+
const sk = hexToBytes(sharedSecret);
|
|
55
|
+
const remotePub = hexToBytes(remoteRatchetPublic);
|
|
56
|
+
const ratchetPriv = (0, utils_js_1.randomBytes)(32);
|
|
57
|
+
const ratchetPub = ed25519_js_1.x25519.getPublicKey(ratchetPriv);
|
|
58
|
+
const dhOut = ed25519_js_1.x25519.getSharedSecret(ratchetPriv, remotePub);
|
|
59
|
+
const derived = (0, hkdf_js_1.hkdf)(sha2_js_1.sha256, dhOut, sk, new TextEncoder().encode("double-ratchet-root"), 64);
|
|
60
|
+
const rootKey = derived.subarray(0, 32);
|
|
61
|
+
const sendingChainKey = derived.subarray(32, 64);
|
|
62
|
+
return new DoubleRatchet({
|
|
63
|
+
sendingRatchetPrivate: bytesToHex(ratchetPriv),
|
|
64
|
+
sendingRatchetPublic: bytesToHex(ratchetPub),
|
|
65
|
+
receivingRatchetPublic: remoteRatchetPublic,
|
|
66
|
+
rootKey: bytesToHex(rootKey),
|
|
67
|
+
sendingChainKey: bytesToHex(sendingChainKey),
|
|
68
|
+
receivingChainKey: null,
|
|
69
|
+
sendingMessageNumber: 0,
|
|
70
|
+
receivingMessageNumber: 0,
|
|
71
|
+
previousChainLength: 0,
|
|
72
|
+
skippedKeys: {},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
static initBob(sharedSecret, ourRatchetKeyPair) {
|
|
76
|
+
return new DoubleRatchet({
|
|
77
|
+
sendingRatchetPrivate: ourRatchetKeyPair.privateKey,
|
|
78
|
+
sendingRatchetPublic: ourRatchetKeyPair.publicKey,
|
|
79
|
+
receivingRatchetPublic: null,
|
|
80
|
+
rootKey: sharedSecret,
|
|
81
|
+
sendingChainKey: null,
|
|
82
|
+
receivingChainKey: null,
|
|
83
|
+
sendingMessageNumber: 0,
|
|
84
|
+
receivingMessageNumber: 0,
|
|
85
|
+
previousChainLength: 0,
|
|
86
|
+
skippedKeys: {},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
static fromState(state) {
|
|
90
|
+
return new DoubleRatchet({ ...state });
|
|
91
|
+
}
|
|
92
|
+
encrypt(plaintext) {
|
|
93
|
+
if (!this._state.sendingChainKey) {
|
|
94
|
+
throw new Error("Sending chain not initialized — cannot encrypt");
|
|
95
|
+
}
|
|
96
|
+
const chain = new SymmetricRatchet(this._state.sendingChainKey);
|
|
97
|
+
const { messageKey } = chain.next();
|
|
98
|
+
this._state.sendingChainKey = chain.state.chainKey;
|
|
99
|
+
const header = {
|
|
100
|
+
publicKey: this._state.sendingRatchetPublic,
|
|
101
|
+
previousChainLength: this._state.previousChainLength,
|
|
102
|
+
messageNumber: this._state.sendingMessageNumber,
|
|
103
|
+
};
|
|
104
|
+
this._state.sendingMessageNumber++;
|
|
105
|
+
const nonce = (0, utils_js_1.randomBytes)(NONCE_LEN);
|
|
106
|
+
const pt = plaintext instanceof Uint8Array
|
|
107
|
+
? plaintext
|
|
108
|
+
: Buffer.from(plaintext, "utf8");
|
|
109
|
+
const aad = Buffer.from(JSON.stringify(header), "utf8");
|
|
110
|
+
const cipher = (0, chacha_js_1.xchacha20poly1305)(messageKey, nonce, aad);
|
|
111
|
+
const ct = cipher.encrypt(pt);
|
|
112
|
+
const packed = new Uint8Array(NONCE_LEN + ct.length);
|
|
113
|
+
packed.set(nonce);
|
|
114
|
+
packed.set(ct, NONCE_LEN);
|
|
115
|
+
return {
|
|
116
|
+
header,
|
|
117
|
+
ciphertext: Buffer.from(packed).toString("base64"),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
decrypt(header, ciphertext) {
|
|
121
|
+
const skipKey = `${header.publicKey}:${header.messageNumber}`;
|
|
122
|
+
if (this._state.skippedKeys[skipKey]) {
|
|
123
|
+
const mk = hexToBytes(this._state.skippedKeys[skipKey]);
|
|
124
|
+
delete this._state.skippedKeys[skipKey];
|
|
125
|
+
return this._decryptWithKey(mk, header, ciphertext);
|
|
126
|
+
}
|
|
127
|
+
if (header.publicKey !== this._state.receivingRatchetPublic) {
|
|
128
|
+
if (this._state.receivingChainKey !== null) {
|
|
129
|
+
this._skipMessages(this._state.receivingRatchetPublic, this._state.receivingMessageNumber, header.previousChainLength);
|
|
130
|
+
}
|
|
131
|
+
this._dhRatchetStep(header.publicKey);
|
|
132
|
+
}
|
|
133
|
+
this._skipMessages(header.publicKey, this._state.receivingMessageNumber, header.messageNumber);
|
|
134
|
+
const chain = new SymmetricRatchet(this._state.receivingChainKey);
|
|
135
|
+
const { messageKey } = chain.next();
|
|
136
|
+
this._state.receivingChainKey = chain.state.chainKey;
|
|
137
|
+
this._state.receivingMessageNumber++;
|
|
138
|
+
return this._decryptWithKey(messageKey, header, ciphertext);
|
|
139
|
+
}
|
|
140
|
+
_decryptWithKey(messageKey, header, ciphertext) {
|
|
141
|
+
const raw = Buffer.from(ciphertext, "base64");
|
|
142
|
+
if (raw.length < NONCE_LEN + 16) {
|
|
143
|
+
throw new Error("Ciphertext too short");
|
|
144
|
+
}
|
|
145
|
+
const nonce = raw.subarray(0, NONCE_LEN);
|
|
146
|
+
const ct = raw.subarray(NONCE_LEN);
|
|
147
|
+
const aad = Buffer.from(JSON.stringify(header), "utf8");
|
|
148
|
+
const cipher = (0, chacha_js_1.xchacha20poly1305)(messageKey, nonce, aad);
|
|
149
|
+
return cipher.decrypt(ct);
|
|
150
|
+
}
|
|
151
|
+
_dhRatchetStep(newRemotePublic) {
|
|
152
|
+
const remotePub = hexToBytes(newRemotePublic);
|
|
153
|
+
const rootKey = hexToBytes(this._state.rootKey);
|
|
154
|
+
this._state.previousChainLength = this._state.sendingMessageNumber;
|
|
155
|
+
this._state.sendingMessageNumber = 0;
|
|
156
|
+
this._state.receivingMessageNumber = 0;
|
|
157
|
+
this._state.receivingRatchetPublic = newRemotePublic;
|
|
158
|
+
const dhRecv = ed25519_js_1.x25519.getSharedSecret(hexToBytes(this._state.sendingRatchetPrivate), remotePub);
|
|
159
|
+
const derivedRecv = (0, hkdf_js_1.hkdf)(sha2_js_1.sha256, dhRecv, rootKey, new TextEncoder().encode("double-ratchet-root"), 64);
|
|
160
|
+
const newRootKey = derivedRecv.subarray(0, 32);
|
|
161
|
+
this._state.receivingChainKey = bytesToHex(derivedRecv.subarray(32, 64));
|
|
162
|
+
const newPriv = (0, utils_js_1.randomBytes)(32);
|
|
163
|
+
const newPub = ed25519_js_1.x25519.getPublicKey(newPriv);
|
|
164
|
+
this._state.sendingRatchetPrivate = bytesToHex(newPriv);
|
|
165
|
+
this._state.sendingRatchetPublic = bytesToHex(newPub);
|
|
166
|
+
const dhSend = ed25519_js_1.x25519.getSharedSecret(newPriv, remotePub);
|
|
167
|
+
const derivedSend = (0, hkdf_js_1.hkdf)(sha2_js_1.sha256, dhSend, newRootKey, new TextEncoder().encode("double-ratchet-root"), 64);
|
|
168
|
+
this._state.rootKey = bytesToHex(derivedSend.subarray(0, 32));
|
|
169
|
+
this._state.sendingChainKey = bytesToHex(derivedSend.subarray(32, 64));
|
|
170
|
+
}
|
|
171
|
+
_skipMessages(publicKey, start, until) {
|
|
172
|
+
if (until - start > MAX_SKIP) {
|
|
173
|
+
throw new Error(`Too many skipped messages: ${until - start} exceeds max ${MAX_SKIP}`);
|
|
174
|
+
}
|
|
175
|
+
if (!this._state.receivingChainKey)
|
|
176
|
+
return;
|
|
177
|
+
const chain = new SymmetricRatchet(this._state.receivingChainKey, start);
|
|
178
|
+
for (let i = start; i < until; i++) {
|
|
179
|
+
const { messageKey, index } = chain.next();
|
|
180
|
+
const key = `${publicKey}:${index}`;
|
|
181
|
+
this._state.skippedKeys[key] = bytesToHex(messageKey);
|
|
182
|
+
}
|
|
183
|
+
this._state.receivingChainKey = chain.state.chainKey;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
exports.DoubleRatchet = DoubleRatchet;
|
|
187
|
+
//# sourceMappingURL=ratchet.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface Share {
|
|
2
|
+
index: number;
|
|
3
|
+
value: string;
|
|
4
|
+
}
|
|
5
|
+
export interface SplitResult {
|
|
6
|
+
shares: Share[];
|
|
7
|
+
threshold: number;
|
|
8
|
+
algorithm: "shamir-ed25519";
|
|
9
|
+
}
|
|
10
|
+
export interface FeldmanCommitments {
|
|
11
|
+
commitments: string[];
|
|
12
|
+
algorithm: "feldman-vss-ed25519";
|
|
13
|
+
}
|
|
14
|
+
export declare function splitSecret(secret: string, n: number, threshold: number): SplitResult;
|
|
15
|
+
export declare function combineShares(shares: Share[]): string;
|
|
16
|
+
export declare function generateFeldmanCommitments(coefficients: string[]): FeldmanCommitments;
|
|
17
|
+
export declare function verifyFeldmanShare(share: Share, commitments: FeldmanCommitments): boolean;
|
|
18
|
+
export declare function splitSecretWithCommitments(secret: string, n: number, threshold: number): {
|
|
19
|
+
shares: Share[];
|
|
20
|
+
commitments: FeldmanCommitments;
|
|
21
|
+
threshold: number;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=threshold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threshold.d.ts","sourceRoot":"","sources":["../../src/protocols/threshold.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,KAAK;IAEpB,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,EAAE,MAAM,CAAC;CACf;AAGD,MAAM,WAAW,WAAW;IAE1B,MAAM,EAAE,KAAK,EAAE,CAAC;IAEhB,SAAS,EAAE,MAAM,CAAC;IAElB,SAAS,EAAE,gBAAgB,CAAC;CAC7B;AAGD,MAAM,WAAW,kBAAkB;IAEjC,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB,SAAS,EAAE,qBAAqB,CAAC;CAClC;AAgID,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,CAAC,EAAE,MAAM,EACT,SAAS,EAAE,MAAM,GAChB,WAAW,CA0Cb;AAQD,wBAAgB,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAiCrD;AAaD,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,EAAE,GACrB,kBAAkB,CAiBpB;AAYD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,kBAAkB,GAC9B,OAAO,CAsBT;AAUD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,CAAC,EAAE,MAAM,EACT,SAAS,EAAE,MAAM,GAChB;IAED,MAAM,EAAE,KAAK,EAAE,CAAC;IAEhB,WAAW,EAAE,kBAAkB,CAAC;IAEhC,SAAS,EAAE,MAAM,CAAC;CACnB,CA0CA"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.splitSecret = splitSecret;
|
|
4
|
+
exports.combineShares = combineShares;
|
|
5
|
+
exports.generateFeldmanCommitments = generateFeldmanCommitments;
|
|
6
|
+
exports.verifyFeldmanShare = verifyFeldmanShare;
|
|
7
|
+
exports.splitSecretWithCommitments = splitSecretWithCommitments;
|
|
8
|
+
const ed25519_js_1 = require("@noble/curves/ed25519.js");
|
|
9
|
+
const utils_js_1 = require("@noble/ciphers/utils.js");
|
|
10
|
+
const P = BigInt("7237005577332262213973186563042994240857116359379907606001950938285454250989");
|
|
11
|
+
const HEX_RE = /^[0-9a-fA-F]*$/;
|
|
12
|
+
function bytesToHex(bytes) {
|
|
13
|
+
return Buffer.from(bytes).toString("hex");
|
|
14
|
+
}
|
|
15
|
+
function scalarToHex(scalar) {
|
|
16
|
+
const hex = scalar.toString(16).padStart(64, "0");
|
|
17
|
+
return hex;
|
|
18
|
+
}
|
|
19
|
+
function hexToScalar(hex) {
|
|
20
|
+
if (!HEX_RE.test(hex)) {
|
|
21
|
+
throw new Error("Invalid hex string");
|
|
22
|
+
}
|
|
23
|
+
return BigInt("0x" + hex);
|
|
24
|
+
}
|
|
25
|
+
function randomFieldElement() {
|
|
26
|
+
const raw = (0, utils_js_1.randomBytes)(48);
|
|
27
|
+
let value = BigInt(0);
|
|
28
|
+
for (let i = 0; i < raw.length; i++) {
|
|
29
|
+
value = (value * BigInt(256) + BigInt(raw[i])) % P;
|
|
30
|
+
}
|
|
31
|
+
if (value === BigInt(0))
|
|
32
|
+
value = BigInt(1);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function mod(a, m = P) {
|
|
36
|
+
return ((a % m) + m) % m;
|
|
37
|
+
}
|
|
38
|
+
function modAdd(a, b) {
|
|
39
|
+
return mod(a + b);
|
|
40
|
+
}
|
|
41
|
+
function modMul(a, b) {
|
|
42
|
+
return mod(a * b);
|
|
43
|
+
}
|
|
44
|
+
function modSub(a, b) {
|
|
45
|
+
return mod(a - b);
|
|
46
|
+
}
|
|
47
|
+
function modInverse(a) {
|
|
48
|
+
return modPow(a, P - BigInt(2));
|
|
49
|
+
}
|
|
50
|
+
function modPow(base, exp) {
|
|
51
|
+
let result = BigInt(1);
|
|
52
|
+
base = mod(base);
|
|
53
|
+
while (exp > BigInt(0)) {
|
|
54
|
+
if (exp & BigInt(1)) {
|
|
55
|
+
result = modMul(result, base);
|
|
56
|
+
}
|
|
57
|
+
exp >>= BigInt(1);
|
|
58
|
+
base = modMul(base, base);
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
function evaluatePolynomial(coefficients, x) {
|
|
63
|
+
let result = BigInt(0);
|
|
64
|
+
let power = BigInt(1);
|
|
65
|
+
for (const coeff of coefficients) {
|
|
66
|
+
result = modAdd(result, modMul(coeff, power));
|
|
67
|
+
power = modMul(power, x);
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
function splitSecret(secret, n, threshold) {
|
|
72
|
+
if (threshold < 2) {
|
|
73
|
+
throw new Error("Threshold must be at least 2");
|
|
74
|
+
}
|
|
75
|
+
if (n < threshold) {
|
|
76
|
+
throw new Error("Number of shares must be >= threshold");
|
|
77
|
+
}
|
|
78
|
+
if (n > 255) {
|
|
79
|
+
throw new Error("Maximum 255 shares supported");
|
|
80
|
+
}
|
|
81
|
+
const secretScalar = hexToScalar(secret);
|
|
82
|
+
if (secretScalar >= P) {
|
|
83
|
+
throw new Error("Secret must be less than the Ed25519 group order; reduce or derive it first");
|
|
84
|
+
}
|
|
85
|
+
const coefficients = [secretScalar];
|
|
86
|
+
for (let i = 1; i < threshold; i++) {
|
|
87
|
+
coefficients.push(randomFieldElement());
|
|
88
|
+
}
|
|
89
|
+
const shares = [];
|
|
90
|
+
for (let i = 1; i <= n; i++) {
|
|
91
|
+
const x = BigInt(i);
|
|
92
|
+
const y = evaluatePolynomial(coefficients, x);
|
|
93
|
+
shares.push({
|
|
94
|
+
index: i,
|
|
95
|
+
value: scalarToHex(y),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
shares,
|
|
100
|
+
threshold,
|
|
101
|
+
algorithm: "shamir-ed25519",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function combineShares(shares) {
|
|
105
|
+
if (shares.length < 2) {
|
|
106
|
+
throw new Error("Need at least 2 shares to reconstruct");
|
|
107
|
+
}
|
|
108
|
+
const indices = new Set(shares.map((s) => s.index));
|
|
109
|
+
if (indices.size !== shares.length) {
|
|
110
|
+
throw new Error("Duplicate share indices detected");
|
|
111
|
+
}
|
|
112
|
+
let secret = BigInt(0);
|
|
113
|
+
for (let i = 0; i < shares.length; i++) {
|
|
114
|
+
const xi = BigInt(shares[i].index);
|
|
115
|
+
const yi = hexToScalar(shares[i].value);
|
|
116
|
+
let numerator = BigInt(1);
|
|
117
|
+
let denominator = BigInt(1);
|
|
118
|
+
for (let j = 0; j < shares.length; j++) {
|
|
119
|
+
if (i === j)
|
|
120
|
+
continue;
|
|
121
|
+
const xj = BigInt(shares[j].index);
|
|
122
|
+
numerator = modMul(numerator, mod(-xj));
|
|
123
|
+
denominator = modMul(denominator, modSub(xi, xj));
|
|
124
|
+
}
|
|
125
|
+
const lagrangeCoeff = modMul(numerator, modInverse(denominator));
|
|
126
|
+
secret = modAdd(secret, modMul(yi, lagrangeCoeff));
|
|
127
|
+
}
|
|
128
|
+
return scalarToHex(secret);
|
|
129
|
+
}
|
|
130
|
+
function generateFeldmanCommitments(coefficients) {
|
|
131
|
+
if (coefficients.length < 2) {
|
|
132
|
+
throw new Error("Need at least 2 coefficients (threshold >= 2)");
|
|
133
|
+
}
|
|
134
|
+
const commitments = [];
|
|
135
|
+
for (const coeff of coefficients) {
|
|
136
|
+
const scalar = hexToScalar(coeff);
|
|
137
|
+
const point = ed25519_js_1.ed25519.Point.BASE.multiply(mod(scalar));
|
|
138
|
+
commitments.push(bytesToHex(point.toBytes()));
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
commitments,
|
|
142
|
+
algorithm: "feldman-vss-ed25519",
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function verifyFeldmanShare(share, commitments) {
|
|
146
|
+
const { index, value } = share;
|
|
147
|
+
const shareScalar = hexToScalar(value);
|
|
148
|
+
const lhs = ed25519_js_1.ed25519.Point.BASE.multiply(mod(shareScalar));
|
|
149
|
+
const x = BigInt(index);
|
|
150
|
+
let rhs = ed25519_js_1.ed25519.Point.ZERO;
|
|
151
|
+
let power = BigInt(1);
|
|
152
|
+
for (const commitHex of commitments.commitments) {
|
|
153
|
+
const commitPoint = ed25519_js_1.ed25519.Point.fromHex(commitHex);
|
|
154
|
+
const term = commitPoint.multiply(power);
|
|
155
|
+
rhs = rhs.add(term);
|
|
156
|
+
power = modMul(power, x);
|
|
157
|
+
}
|
|
158
|
+
return lhs.equals(rhs);
|
|
159
|
+
}
|
|
160
|
+
function splitSecretWithCommitments(secret, n, threshold) {
|
|
161
|
+
if (threshold < 2) {
|
|
162
|
+
throw new Error("Threshold must be at least 2");
|
|
163
|
+
}
|
|
164
|
+
if (n < threshold) {
|
|
165
|
+
throw new Error("Number of shares must be >= threshold");
|
|
166
|
+
}
|
|
167
|
+
if (n > 255) {
|
|
168
|
+
throw new Error("Maximum 255 shares supported");
|
|
169
|
+
}
|
|
170
|
+
const secretScalar = hexToScalar(secret);
|
|
171
|
+
if (secretScalar >= P) {
|
|
172
|
+
throw new Error("Secret must be less than the Ed25519 group order; reduce or derive it first");
|
|
173
|
+
}
|
|
174
|
+
const coefficients = [secretScalar];
|
|
175
|
+
for (let i = 1; i < threshold; i++) {
|
|
176
|
+
coefficients.push(randomFieldElement());
|
|
177
|
+
}
|
|
178
|
+
const shares = [];
|
|
179
|
+
for (let i = 1; i <= n; i++) {
|
|
180
|
+
const x = BigInt(i);
|
|
181
|
+
const y = evaluatePolynomial(coefficients, x);
|
|
182
|
+
shares.push({
|
|
183
|
+
index: i,
|
|
184
|
+
value: scalarToHex(y),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
const coeffHexes = coefficients.map(scalarToHex);
|
|
188
|
+
const commitments = generateFeldmanCommitments(coeffHexes);
|
|
189
|
+
return { shares, commitments, threshold };
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=threshold.js.map
|