refactor: key type as parameter

This commit is contained in:
Richard Ramos 2023-11-20 15:56:52 -04:00
parent 83d6d553a3
commit 62836e847b
No known key found for this signature in database
GPG Key ID: 1CE87DB518195760
10 changed files with 173 additions and 117 deletions

View File

@ -1,15 +1,13 @@
import { ChaCha20Poly1305 } from "@stablelib/chacha20poly1305"; import { ChaCha20Poly1305 } from "@stablelib/chacha20poly1305";
import { Hash } from "@stablelib/hash"; import { Hash } from "@stablelib/hash";
import { HKDF as hkdf } from "@stablelib/hkdf"; import { HKDF as hkdf } from "@stablelib/hkdf";
import { RandomSource } from "@stablelib/random";
import { hash } from "@stablelib/sha256"; import { hash } from "@stablelib/sha256";
import * as x25519 from "@stablelib/x25519";
import { concat as uint8ArrayConcat } from "uint8arrays/concat"; import { concat as uint8ArrayConcat } from "uint8arrays/concat";
import type { bytes32 } from "./@types/basic.js"; import type { bytes32 } from "./@types/basic.js";
import type { KeyPair } from "./@types/keypair.js"; import type { KeyPair } from "./@types/keypair.js";
export const Curve25519KeySize = x25519.PUBLIC_KEY_LENGTH;
/** /**
* Generate hash using SHA2-256 * Generate hash using SHA2-256
* @param data data to hash * @param data data to hash
@ -19,21 +17,6 @@ export function hashSHA256(data: Uint8Array): Uint8Array {
return hash(data); return hash(data);
} }
/**
* Convert an Uint8Array into a 32-byte value. If the input data length is different
* from 32, throw an error. This is used mostly as a validation function to ensure
* that an Uint8Array represents a valid x25519 key
* @param s input data
* @return 32-byte key
*/
export function intoCurve25519Key(s: Uint8Array): bytes32 {
if (s.length != x25519.PUBLIC_KEY_LENGTH) {
throw new Error("invalid public key length");
}
return s;
}
/** /**
* HKDF key derivation function using SHA256 * HKDF key derivation function using SHA256
* @param ck chaining key * @param ck chaining key
@ -59,33 +42,6 @@ export function HKDF(
return result; return result;
} }
/**
* Generate a random keypair
* @returns Keypair
*/
export function generateX25519KeyPair(): KeyPair {
const keypair = x25519.generateKeyPair();
return {
publicKey: keypair.publicKey,
privateKey: keypair.secretKey,
};
}
/**
* Generate x25519 keypair using an input seed
* @param seed 32-byte secret
* @returns Keypair
*/
export function generateX25519KeyPairFromSeed(seed: bytes32): KeyPair {
const keypair = x25519.generateKeyPairFromSeed(seed);
return {
publicKey: keypair.publicKey,
privateKey: keypair.secretKey,
};
}
/** /**
* Encrypt and authenticate data using ChaCha20-Poly1305 * Encrypt and authenticate data using ChaCha20-Poly1305
* @param plaintext data to encrypt * @param plaintext data to encrypt
@ -123,27 +79,6 @@ export function chaCha20Poly1305Decrypt(
return ctx.open(nonce, ciphertext, ad); return ctx.open(nonce, ciphertext, ad);
} }
/**
* Perform a DiffieHellman key exchange
* @param privateKey x25519 private key
* @param publicKey x25519 public key
* @returns shared secret
*/
export function dh(privateKey: bytes32, publicKey: bytes32): bytes32 {
try {
const derivedU8 = x25519.sharedKey(privateKey, publicKey);
if (derivedU8.length === 32) {
return derivedU8;
}
return derivedU8.subarray(0, 32);
} catch (e) {
console.error(e);
return new Uint8Array(32);
}
}
/** /**
* Generates a random static key commitment using a public key pk for randomness r as H(pk || s) * Generates a random static key commitment using a public key pk for randomness r as H(pk || s)
* @param publicKey x25519 public key * @param publicKey x25519 public key
@ -153,3 +88,43 @@ export function dh(privateKey: bytes32, publicKey: bytes32): bytes32 {
export function commitPublicKey(publicKey: bytes32, r: Uint8Array): bytes32 { export function commitPublicKey(publicKey: bytes32, r: Uint8Array): bytes32 {
return hashSHA256(uint8ArrayConcat([publicKey, r])); return hashSHA256(uint8ArrayConcat([publicKey, r]));
} }
/**
* Represents a key uses for DiffieHellman key exchange
*/
export interface DHKey {
/**
* Convert an Uint8Array into a 32-byte value. If the input data length is different
* from 32, throw an error. This is used mostly as a validation function to ensure
* that an Uint8Array represents a valid key
* @param s input data
* @return 32-byte key
*/
intoKey(s: Uint8Array): bytes32;
/**
* Get key length
*/
DHLen(): number;
/**
* Perform a DiffieHellman key exchange
* @param privateKey private key
* @param publicKey public key
* @returns shared secret
*/
DH(privateKey: bytes32, publicKey: bytes32): bytes32;
/**
* Generate a random keypair
* @returns Keypair
*/
generateKeyPair(prng?: RandomSource): KeyPair;
/**
* Generate keypair using an input seed
* @param seed 32-byte secret
* @returns Keypair
*/
generateKeyPairFromSeed(seed: bytes32): KeyPair;
}

52
src/dh25519.ts Normal file
View File

@ -0,0 +1,52 @@
import * as x25519 from "@stablelib/x25519";
import type { bytes32 } from "./@types/basic.js";
import type { KeyPair } from "./@types/keypair.js";
import { DHKey } from "./crypto.js";
export class DH25519 implements DHKey {
intoKey(s: Uint8Array): bytes32 {
if (s.length != x25519.PUBLIC_KEY_LENGTH) {
throw new Error("invalid public key length");
}
return s;
}
generateKeyPair(): KeyPair {
const keypair = x25519.generateKeyPair();
return {
publicKey: keypair.publicKey,
privateKey: keypair.secretKey,
};
}
generateKeyPairFromSeed(seed: bytes32): KeyPair {
const keypair = x25519.generateKeyPairFromSeed(seed);
return {
publicKey: keypair.publicKey,
privateKey: keypair.secretKey,
};
}
DH(privateKey: bytes32, publicKey: bytes32): bytes32 {
try {
const derivedU8 = x25519.sharedKey(privateKey, publicKey);
if (derivedU8.length === 32) {
return derivedU8;
}
return derivedU8.subarray(0, 32);
} catch (e) {
console.error(e);
return new Uint8Array(32);
}
}
DHLen(): number {
return x25519.PUBLIC_KEY_LENGTH;
}
}

View File

@ -5,7 +5,7 @@ import { equals as uint8ArrayEquals } from "uint8arrays/equals";
import { bytes32 } from "./@types/basic.js"; import { bytes32 } from "./@types/basic.js";
import { MessageNametag } from "./@types/handshake.js"; import { MessageNametag } from "./@types/handshake.js";
import type { KeyPair } from "./@types/keypair.js"; import type { KeyPair } from "./@types/keypair.js";
import { Curve25519KeySize, dh, generateX25519KeyPair, HKDF, intoCurve25519Key } from "./crypto.js"; import { HKDF } from "./crypto.js";
import { MessageNametagLength } from "./messagenametag.js"; import { MessageNametagLength } from "./messagenametag.js";
import { SymmetricState } from "./noise.js"; import { SymmetricState } from "./noise.js";
import { HandshakePattern, MessageDirection, NoiseTokens, PreMessagePattern } from "./patterns.js"; import { HandshakePattern, MessageDirection, NoiseTokens, PreMessagePattern } from "./patterns.js";
@ -212,7 +212,7 @@ export class HandshakeState {
// We check if current key is encrypted or not. We assume pre-message public keys are all unencrypted on users' end // We check if current key is encrypted or not. We assume pre-message public keys are all unencrypted on users' end
if (currPK.flag == 0) { if (currPK.flag == 0) {
// Sets re and calls MixHash(re.public_key). // Sets re and calls MixHash(re.public_key).
this.re = intoCurve25519Key(currPK.pk); this.re = this.handshakePattern.dhKey.intoKey(currPK.pk);
this.ss.mixHash(this.re); this.ss.mixHash(this.re);
} else { } else {
throw new Error("noise read e, incorrect encryption flag for pre-message public key"); throw new Error("noise read e, incorrect encryption flag for pre-message public key");
@ -223,7 +223,7 @@ export class HandshakeState {
// When writing, the user is sending a public key, // When writing, the user is sending a public key,
// We check that the public part corresponds to the set local key and we call MixHash(e.public_key). // We check that the public part corresponds to the set local key and we call MixHash(e.public_key).
if (this.e && uint8ArrayEquals(this.e.publicKey, intoCurve25519Key(currPK.pk))) { if (this.e && uint8ArrayEquals(this.e.publicKey, this.handshakePattern.dhKey.intoKey(currPK.pk))) {
this.ss.mixHash(this.e.publicKey); this.ss.mixHash(this.e.publicKey);
} else { } else {
throw new Error("noise pre-message e key doesn't correspond to locally set e key pair"); throw new Error("noise pre-message e key doesn't correspond to locally set e key pair");
@ -256,7 +256,7 @@ export class HandshakeState {
// We check if current key is encrypted or not. We assume pre-message public keys are all unencrypted on users' end // We check if current key is encrypted or not. We assume pre-message public keys are all unencrypted on users' end
if (currPK.flag == 0) { if (currPK.flag == 0) {
// Sets re and calls MixHash(re.public_key). // Sets re and calls MixHash(re.public_key).
this.rs = intoCurve25519Key(currPK.pk); this.rs = this.handshakePattern.dhKey.intoKey(currPK.pk);
this.ss.mixHash(this.rs); this.ss.mixHash(this.rs);
} else { } else {
throw new Error("noise read s, incorrect encryption flag for pre-message public key"); throw new Error("noise read s, incorrect encryption flag for pre-message public key");
@ -268,7 +268,7 @@ export class HandshakeState {
// If writing, it means that the user is sending a public key, // If writing, it means that the user is sending a public key,
// We check that the public part corresponds to the set local key and we call MixHash(s.public_key). // We check that the public part corresponds to the set local key and we call MixHash(s.public_key).
if (this.s && uint8ArrayEquals(this.s.publicKey, intoCurve25519Key(currPK.pk))) { if (this.s && uint8ArrayEquals(this.s.publicKey, this.handshakePattern.dhKey.intoKey(currPK.pk))) {
this.ss.mixHash(this.s.publicKey); this.ss.mixHash(this.s.publicKey);
} else { } else {
throw new Error("noise pre-message s key doesn't correspond to locally set s key pair"); throw new Error("noise pre-message s key doesn't correspond to locally set s key pair");
@ -358,14 +358,14 @@ export class HandshakeState {
if (currPK.flag == 0) { if (currPK.flag == 0) {
// Unencrypted Public Key // Unencrypted Public Key
// Sets re and calls MixHash(re.public_key). // Sets re and calls MixHash(re.public_key).
this.re = intoCurve25519Key(currPK.pk); this.re = this.handshakePattern.dhKey.intoKey(currPK.pk);
this.ss.mixHash(this.re); this.ss.mixHash(this.re);
// The following is out of specification: we call decryptAndHash for encrypted ephemeral keys, similarly as happens for (encrypted) static keys // The following is out of specification: we call decryptAndHash for encrypted ephemeral keys, similarly as happens for (encrypted) static keys
} else if (currPK.flag == 1) { } else if (currPK.flag == 1) {
// Encrypted public key // Encrypted public key
// Decrypts re, sets re and calls MixHash(re.public_key). // Decrypts re, sets re and calls MixHash(re.public_key).
this.re = intoCurve25519Key(this.ss.decryptAndHash(currPK.pk)); this.re = this.handshakePattern.dhKey.intoKey(this.ss.decryptAndHash(currPK.pk));
} else { } else {
throw new Error("noise read e, incorrect encryption flag for public key"); throw new Error("noise read e, incorrect encryption flag for public key");
} }
@ -386,7 +386,7 @@ export class HandshakeState {
log("noise write e"); log("noise write e");
// We generate a new ephemeral keypair // We generate a new ephemeral keypair
this.e = generateX25519KeyPair(); this.e = this.handshakePattern.dhKey.generateKeyPair();
// We update the state // We update the state
this.ss.mixHash(this.e.publicKey); this.ss.mixHash(this.e.publicKey);
@ -420,12 +420,12 @@ export class HandshakeState {
if (currPK.flag == 0) { if (currPK.flag == 0) {
// Unencrypted Public Key // Unencrypted Public Key
// Sets re and calls MixHash(re.public_key). // Sets re and calls MixHash(re.public_key).
this.rs = intoCurve25519Key(currPK.pk); this.rs = this.handshakePattern.dhKey.intoKey(currPK.pk);
this.ss.mixHash(this.rs); this.ss.mixHash(this.rs);
} else if (currPK.flag == 1) { } else if (currPK.flag == 1) {
// Encrypted public key // Encrypted public key
// Decrypts rs, sets rs and calls MixHash(rs.public_key). // Decrypts rs, sets rs and calls MixHash(rs.public_key).
this.rs = intoCurve25519Key(this.ss.decryptAndHash(currPK.pk)); this.rs = this.handshakePattern.dhKey.intoKey(this.ss.decryptAndHash(currPK.pk));
} else { } else {
throw new Error("noise read s, incorrect encryption flag for public key"); throw new Error("noise read s, incorrect encryption flag for public key");
} }
@ -449,7 +449,7 @@ export class HandshakeState {
// We add the (encrypted) static public key to the Waku payload // We add the (encrypted) static public key to the Waku payload
// Note that encS = (Enc(s) || tag) if encryption key is set, otherwise encS = s. // Note that encS = (Enc(s) || tag) if encryption key is set, otherwise encS = s.
// We distinguish these two cases by checking length of encryption and we set the proper encryption flag // We distinguish these two cases by checking length of encryption and we set the proper encryption flag
if (encS.length > Curve25519KeySize) { if (encS.length > this.handshakePattern.dhKey.DHLen()) {
outHandshakeMessage.push(new NoisePublicKey(1, encS)); outHandshakeMessage.push(new NoisePublicKey(1, encS));
} else { } else {
outHandshakeMessage.push(new NoisePublicKey(0, encS)); outHandshakeMessage.push(new NoisePublicKey(0, encS));
@ -478,7 +478,7 @@ export class HandshakeState {
} }
// Calls MixKey(DH(e, re)). // Calls MixKey(DH(e, re)).
this.ss.mixKey(dh(this.e.privateKey, this.re)); this.ss.mixKey(this.handshakePattern.dhKey.DH(this.e.privateKey, this.re));
break; break;
case NoiseTokens.es: case NoiseTokens.es:
@ -493,13 +493,13 @@ export class HandshakeState {
throw new Error("local or remote ephemeral/static key not set"); throw new Error("local or remote ephemeral/static key not set");
} }
this.ss.mixKey(dh(this.e.privateKey, this.rs)); this.ss.mixKey(this.handshakePattern.dhKey.DH(this.e.privateKey, this.rs));
} else { } else {
if (!this.re || !this.s) { if (!this.re || !this.s) {
throw new Error("local or remote ephemeral/static key not set"); throw new Error("local or remote ephemeral/static key not set");
} }
this.ss.mixKey(dh(this.s.privateKey, this.re)); this.ss.mixKey(this.handshakePattern.dhKey.DH(this.s.privateKey, this.re));
} }
break; break;
@ -515,13 +515,13 @@ export class HandshakeState {
throw new Error("local or remote ephemeral/static key not set"); throw new Error("local or remote ephemeral/static key not set");
} }
this.ss.mixKey(dh(this.s.privateKey, this.re)); this.ss.mixKey(this.handshakePattern.dhKey.DH(this.s.privateKey, this.re));
} else { } else {
if (!this.rs || !this.e) { if (!this.rs || !this.e) {
throw new Error("local or remote ephemeral/static key not set"); throw new Error("local or remote ephemeral/static key not set");
} }
this.ss.mixKey(dh(this.e.privateKey, this.rs)); this.ss.mixKey(this.handshakePattern.dhKey.DH(this.e.privateKey, this.rs));
} }
break; break;
@ -536,7 +536,7 @@ export class HandshakeState {
} }
// Calls MixKey(DH(s, rs)). // Calls MixKey(DH(s, rs)).
this.ss.mixKey(dh(this.s.privateKey, this.rs)); this.ss.mixKey(this.handshakePattern.dhKey.DH(this.s.privateKey, this.rs));
break; break;
} }
} }

View File

@ -3,7 +3,8 @@ import { randomBytes } from "@stablelib/random";
import { expect } from "chai"; import { expect } from "chai";
import { equals as uint8ArrayEquals } from "uint8arrays/equals"; import { equals as uint8ArrayEquals } from "uint8arrays/equals";
import { chaCha20Poly1305Encrypt, dh, generateX25519KeyPair } from "./crypto"; import { chaCha20Poly1305Encrypt } from "./crypto";
import { DH25519 } from "./dh25519";
import { Handshake, HandshakeStepResult } from "./handshake"; import { Handshake, HandshakeStepResult } from "./handshake";
import { MessageNametagBuffer, MessageNametagLength } from "./messagenametag"; import { MessageNametagBuffer, MessageNametagLength } from "./messagenametag";
import { CipherState, createEmptyKey, SymmetricState } from "./noise"; import { CipherState, createEmptyKey, SymmetricState } from "./noise";
@ -31,7 +32,8 @@ function randomChaChaPolyCipherState(rng: HMACDRBG): ChaChaPolyCipherState {
} }
function randomNoisePublicKey(): NoisePublicKey { function randomNoisePublicKey(): NoisePublicKey {
const keypair = generateX25519KeyPair(); const dhKey = new DH25519();
const keypair = dhKey.generateKeyPair();
return new NoisePublicKey(0, keypair.publicKey); return new NoisePublicKey(0, keypair.publicKey);
} }
@ -138,13 +140,15 @@ describe("js-noise", () => {
}); });
it("Noise State Machine: Diffie-Hellman operation", function () { it("Noise State Machine: Diffie-Hellman operation", function () {
const aliceKey = generateX25519KeyPair(); const dhKey = new DH25519();
const bobKey = generateX25519KeyPair();
const aliceKey = dhKey.generateKeyPair();
const bobKey = dhKey.generateKeyPair();
// A Diffie-Hellman operation between Alice's private key and Bob's public key must be equal to // A Diffie-Hellman operation between Alice's private key and Bob's public key must be equal to
// a Diffie-hellman operation between Alice's public key and Bob's private key // a Diffie-hellman operation between Alice's public key and Bob's private key
const dh1 = dh(aliceKey.privateKey, bobKey.publicKey); const dh1 = dhKey.DH(aliceKey.privateKey, bobKey.publicKey);
const dh2 = dh(bobKey.privateKey, aliceKey.publicKey); const dh2 = dhKey.DH(bobKey.privateKey, aliceKey.publicKey);
expect(uint8ArrayEquals(dh1, dh2)).to.be.true; expect(uint8ArrayEquals(dh1, dh2)).to.be.true;
}); });
@ -355,13 +359,14 @@ describe("js-noise", () => {
}); });
it("Noise XX Handshake and message encryption (extended test)", function () { it("Noise XX Handshake and message encryption (extended test)", function () {
const dhKey = new DH25519();
const hsPattern = NoiseHandshakePatterns.Noise_XX_25519_ChaChaPoly_SHA256; const hsPattern = NoiseHandshakePatterns.Noise_XX_25519_ChaChaPoly_SHA256;
// We initialize Alice's and Bob's Handshake State // We initialize Alice's and Bob's Handshake State
const aliceStaticKey = generateX25519KeyPair(); const aliceStaticKey = dhKey.generateKeyPair();
const aliceHS = new Handshake({ hsPattern, staticKey: aliceStaticKey, initiator: true }); const aliceHS = new Handshake({ hsPattern, staticKey: aliceStaticKey, initiator: true });
const bobStaticKey = generateX25519KeyPair(); const bobStaticKey = dhKey.generateKeyPair();
const bobHS = new Handshake({ hsPattern, staticKey: bobStaticKey }); const bobHS = new Handshake({ hsPattern, staticKey: bobStaticKey });
let sentTransportMessage: Uint8Array; let sentTransportMessage: Uint8Array;
@ -466,16 +471,17 @@ describe("js-noise", () => {
}); });
it("Noise XXpsk0 Handhshake and message encryption (short test)", function () { it("Noise XXpsk0 Handhshake and message encryption (short test)", function () {
const dhKey = new DH25519();
const hsPattern = NoiseHandshakePatterns.Noise_XXpsk0_25519_ChaChaPoly_SHA256; const hsPattern = NoiseHandshakePatterns.Noise_XXpsk0_25519_ChaChaPoly_SHA256;
// We generate a random psk // We generate a random psk
const psk = randomBytes(32, rng); const psk = randomBytes(32, rng);
// We initialize Alice's and Bob's Handshake State // We initialize Alice's and Bob's Handshake State
const aliceStaticKey = generateX25519KeyPair(); const aliceStaticKey = dhKey.generateKeyPair();
const aliceHS = new Handshake({ hsPattern, staticKey: aliceStaticKey, psk, initiator: true }); const aliceHS = new Handshake({ hsPattern, staticKey: aliceStaticKey, psk, initiator: true });
const bobStaticKey = generateX25519KeyPair(); const bobStaticKey = dhKey.generateKeyPair();
const bobHS = new Handshake({ hsPattern, staticKey: bobStaticKey, psk }); const bobHS = new Handshake({ hsPattern, staticKey: bobStaticKey, psk });
let sentTransportMessage: Uint8Array; let sentTransportMessage: Uint8Array;
@ -561,12 +567,13 @@ describe("js-noise", () => {
}); });
it("Noise K1K1 Handhshake and message encryption (short test)", function () { it("Noise K1K1 Handhshake and message encryption (short test)", function () {
const dhKey = new DH25519();
const hsPattern = NoiseHandshakePatterns.Noise_K1K1_25519_ChaChaPoly_SHA256; const hsPattern = NoiseHandshakePatterns.Noise_K1K1_25519_ChaChaPoly_SHA256;
// We initialize Alice's and Bob's Handshake State // We initialize Alice's and Bob's Handshake State
const aliceStaticKey = generateX25519KeyPair(); const aliceStaticKey = dhKey.generateKeyPair();
const bobStaticKey = generateX25519KeyPair(); const bobStaticKey = dhKey.generateKeyPair();
// This handshake has the following pre-message pattern: // This handshake has the following pre-message pattern:
// -> s // -> s
@ -663,11 +670,12 @@ describe("js-noise", () => {
}); });
it("Noise XK1 Handhshake and message encryption (short test)", function () { it("Noise XK1 Handhshake and message encryption (short test)", function () {
const dhKey = new DH25519();
const hsPattern = NoiseHandshakePatterns.Noise_XK1_25519_ChaChaPoly_SHA256; const hsPattern = NoiseHandshakePatterns.Noise_XK1_25519_ChaChaPoly_SHA256;
// We initialize Alice's and Bob's Handshake State // We initialize Alice's and Bob's Handshake State
const aliceStaticKey = generateX25519KeyPair(); const aliceStaticKey = dhKey.generateKeyPair();
const bobStaticKey = generateX25519KeyPair(); const bobStaticKey = dhKey.generateKeyPair();
// This handshake has the following pre-message pattern: // This handshake has the following pre-message pattern:
// <- s // <- s

View File

@ -4,7 +4,7 @@ import {
NoiseSecureTransferDecoder, NoiseSecureTransferDecoder,
NoiseSecureTransferEncoder, NoiseSecureTransferEncoder,
} from "./codec.js"; } from "./codec.js";
import { generateX25519KeyPair, generateX25519KeyPairFromSeed } from "./crypto.js"; import { DH25519 } from "./dh25519.js";
import { import {
Handshake, Handshake,
HandshakeParameters, HandshakeParameters,
@ -35,7 +35,7 @@ export {
MessageNametagError, MessageNametagError,
StepHandshakeParameters, StepHandshakeParameters,
}; };
export { generateX25519KeyPair, generateX25519KeyPairFromSeed }; export { DH25519 as X25519DHKey };
export { export {
HandshakePattern, HandshakePattern,
MessageDirection, MessageDirection,

View File

@ -7,7 +7,7 @@ import { pEvent } from "p-event";
import { equals as uint8ArrayEquals } from "uint8arrays/equals"; import { equals as uint8ArrayEquals } from "uint8arrays/equals";
import { NoiseHandshakeMessage } from "./codec"; import { NoiseHandshakeMessage } from "./codec";
import { generateX25519KeyPair } from "./crypto"; import { DH25519 } from "./dh25519";
import { MessageNametagBufferSize } from "./messagenametag"; import { MessageNametagBufferSize } from "./messagenametag";
import { ResponderParameters, WakuPairing } from "./pairing"; import { ResponderParameters, WakuPairing } from "./pairing";
@ -66,8 +66,10 @@ describe("js-noise: pairing object", () => {
// ================= // =================
it("should pair", async function () { it("should pair", async function () {
const bobStaticKey = generateX25519KeyPair(); const dhKey = new DH25519();
const aliceStaticKey = generateX25519KeyPair();
const bobStaticKey = dhKey.generateKeyPair();
const aliceStaticKey = dhKey.generateKeyPair();
const recvParameters = new ResponderParameters(); const recvParameters = new ResponderParameters();
const bobPairingObj = new WakuPairing(sender, responder, bobStaticKey, recvParameters); const bobPairingObj = new WakuPairing(sender, responder, bobStaticKey, recvParameters);
@ -112,8 +114,9 @@ describe("js-noise: pairing object", () => {
}); });
it("should timeout", async function () { it("should timeout", async function () {
const bobPairingObj = new WakuPairing(sender, responder, generateX25519KeyPair(), new ResponderParameters()); const dhKey = new DH25519();
const alicePairingObj = new WakuPairing(sender, responder, generateX25519KeyPair(), bobPairingObj.getPairingInfo()); const bobPairingObj = new WakuPairing(sender, responder, dhKey.generateKeyPair(), new ResponderParameters());
const alicePairingObj = new WakuPairing(sender, responder, dhKey.generateKeyPair(), bobPairingObj.getPairingInfo());
const bobExecP1 = bobPairingObj.execute(1000); const bobExecP1 = bobPairingObj.execute(1000);
const aliceExecP1 = alicePairingObj.execute(1000); const aliceExecP1 = alicePairingObj.execute(1000);
@ -130,8 +133,9 @@ describe("js-noise: pairing object", () => {
}); });
it("pairs and `meta` field is encoded", async function () { it("pairs and `meta` field is encoded", async function () {
const bobStaticKey = generateX25519KeyPair(); const dhKey = new DH25519();
const aliceStaticKey = generateX25519KeyPair(); const bobStaticKey = dhKey.generateKeyPair();
const aliceStaticKey = dhKey.generateKeyPair();
// Encode the length of the payload // Encode the length of the payload
// Not a relevant real life example // Not a relevant real life example

View File

@ -14,7 +14,8 @@ import {
NoiseSecureTransferDecoder, NoiseSecureTransferDecoder,
NoiseSecureTransferEncoder, NoiseSecureTransferEncoder,
} from "./codec.js"; } from "./codec.js";
import { commitPublicKey, generateX25519KeyPair } from "./crypto.js"; import { commitPublicKey } from "./crypto.js";
import { DH25519 } from "./dh25519.js";
import { Handshake, HandshakeResult, HandshakeStepResult, MessageNametagError } from "./handshake.js"; import { Handshake, HandshakeResult, HandshakeStepResult, MessageNametagError } from "./handshake.js";
import { MessageNametagLength } from "./messagenametag.js"; import { MessageNametagLength } from "./messagenametag.js";
import { NoiseHandshakePatterns } from "./patterns.js"; import { NoiseHandshakePatterns } from "./patterns.js";
@ -94,7 +95,7 @@ export class WakuPairing {
private responder: IReceiver, private responder: IReceiver,
private myStaticKey: KeyPair, private myStaticKey: KeyPair,
pairingParameters: InitiatorParameters | ResponderParameters, pairingParameters: InitiatorParameters | ResponderParameters,
private myEphemeralKey: KeyPair = generateX25519KeyPair(), private myEphemeralKey: KeyPair = new DH25519().generateKeyPair(),
private readonly encoderParameters: EncoderParameters = {} private readonly encoderParameters: EncoderParameters = {}
) { ) {
this.randomFixLenVal = randomBytes(32, rng); this.randomFixLenVal = randomBytes(32, rng);

View File

@ -2,6 +2,9 @@ import { TAG_LENGTH as ChaChaPolyTagLen } from "@stablelib/chacha20poly1305";
import { Hash } from "@stablelib/hash"; import { Hash } from "@stablelib/hash";
import { SHA256 } from "@stablelib/sha256"; import { SHA256 } from "@stablelib/sha256";
import { DHKey } from "./crypto";
import { DH25519 } from "./dh25519";
/** /**
* The Noise tokens appearing in Noise (pre)message patterns * The Noise tokens appearing in Noise (pre)message patterns
* as in http://www.noiseprotocol.org/noise.html#handshake-pattern-basics * as in http://www.noiseprotocol.org/noise.html#handshake-pattern-basics
@ -72,13 +75,18 @@ export class MessagePattern {
* handshake pre message patterns and the handshake message patterns * handshake pre message patterns and the handshake message patterns
*/ */
export class HandshakePattern { export class HandshakePattern {
public readonly dhKey: DHKey;
constructor( constructor(
public readonly name: string, public readonly name: string,
dhKeyType: new () => DHKey,
public readonly hash: new () => Hash, public readonly hash: new () => Hash,
public readonly tagLen: number, public readonly tagLen: number,
public readonly preMessagePatterns: Array<PreMessagePattern>, public readonly preMessagePatterns: Array<PreMessagePattern>,
public readonly messagePatterns: Array<MessagePattern> public readonly messagePatterns: Array<MessagePattern>
) {} ) {
this.dhKey = new dhKeyType();
}
/** /**
* Check HandshakePattern equality * Check HandshakePattern equality
@ -106,6 +114,7 @@ export class HandshakePattern {
export const NoiseHandshakePatterns: Record<string, HandshakePattern> = { export const NoiseHandshakePatterns: Record<string, HandshakePattern> = {
Noise_K1K1_25519_ChaChaPoly_SHA256: new HandshakePattern( Noise_K1K1_25519_ChaChaPoly_SHA256: new HandshakePattern(
"Noise_K1K1_25519_ChaChaPoly_SHA256", "Noise_K1K1_25519_ChaChaPoly_SHA256",
DH25519,
SHA256, SHA256,
ChaChaPolyTagLen, ChaChaPolyTagLen,
[ [
@ -120,6 +129,7 @@ export const NoiseHandshakePatterns: Record<string, HandshakePattern> = {
), ),
Noise_XK1_25519_ChaChaPoly_SHA256: new HandshakePattern( Noise_XK1_25519_ChaChaPoly_SHA256: new HandshakePattern(
"Noise_XK1_25519_ChaChaPoly_SHA256", "Noise_XK1_25519_ChaChaPoly_SHA256",
DH25519,
SHA256, SHA256,
ChaChaPolyTagLen, ChaChaPolyTagLen,
[new PreMessagePattern(MessageDirection.l, [NoiseTokens.s])], [new PreMessagePattern(MessageDirection.l, [NoiseTokens.s])],
@ -131,6 +141,7 @@ export const NoiseHandshakePatterns: Record<string, HandshakePattern> = {
), ),
Noise_XX_25519_ChaChaPoly_SHA256: new HandshakePattern( Noise_XX_25519_ChaChaPoly_SHA256: new HandshakePattern(
"Noise_XX_25519_ChaChaPoly_SHA256", "Noise_XX_25519_ChaChaPoly_SHA256",
DH25519,
SHA256, SHA256,
ChaChaPolyTagLen, ChaChaPolyTagLen,
[], [],
@ -142,6 +153,7 @@ export const NoiseHandshakePatterns: Record<string, HandshakePattern> = {
), ),
Noise_XXpsk0_25519_ChaChaPoly_SHA256: new HandshakePattern( Noise_XXpsk0_25519_ChaChaPoly_SHA256: new HandshakePattern(
"Noise_XXpsk0_25519_ChaChaPoly_SHA256", "Noise_XXpsk0_25519_ChaChaPoly_SHA256",
DH25519,
SHA256, SHA256,
ChaChaPolyTagLen, ChaChaPolyTagLen,
[], [],
@ -153,6 +165,7 @@ export const NoiseHandshakePatterns: Record<string, HandshakePattern> = {
), ),
Noise_WakuPairing_25519_ChaChaPoly_SHA256: new HandshakePattern( Noise_WakuPairing_25519_ChaChaPoly_SHA256: new HandshakePattern(
"Noise_WakuPairing_25519_ChaChaPoly_SHA256", "Noise_WakuPairing_25519_ChaChaPoly_SHA256",
DH25519,
SHA256, SHA256,
ChaChaPolyTagLen, ChaChaPolyTagLen,
[new PreMessagePattern(MessageDirection.l, [NoiseTokens.e])], [new PreMessagePattern(MessageDirection.l, [NoiseTokens.e])],

View File

@ -2,7 +2,6 @@ import { concat as uint8ArrayConcat } from "uint8arrays/concat";
import { equals as uint8ArrayEquals } from "uint8arrays/equals"; import { equals as uint8ArrayEquals } from "uint8arrays/equals";
import { MessageNametag } from "./@types/handshake.js"; import { MessageNametag } from "./@types/handshake.js";
import { Curve25519KeySize } from "./crypto.js";
import { MessageNametagLength } from "./messagenametag.js"; import { MessageNametagLength } from "./messagenametag.js";
import { NoiseHandshakePatterns, PayloadV2ProtocolIDs } from "./patterns.js"; import { NoiseHandshakePatterns, PayloadV2ProtocolIDs } from "./patterns.js";
import { NoisePublicKey } from "./publickey.js"; import { NoisePublicKey } from "./publickey.js";
@ -140,6 +139,7 @@ export class PayloadV2 {
const pattern = NoiseHandshakePatterns[protocolName]; const pattern = NoiseHandshakePatterns[protocolName];
const tagLen = pattern ? pattern.tagLen : 0; const tagLen = pattern ? pattern.tagLen : 0;
const keySize = pattern ? pattern.dhKey.DHLen() : 0;
i++; i++;
@ -163,13 +163,13 @@ export class PayloadV2 {
const flag = payload[i]; const flag = payload[i];
// If the key is unencrypted, we only read the X coordinate of the EC public key and we deserialize into a Noise Public Key // If the key is unencrypted, we only read the X coordinate of the EC public key and we deserialize into a Noise Public Key
if (flag === 0) { if (flag === 0) {
const pkLen = 1 + Curve25519KeySize; const pkLen = 1 + keySize;
handshakeMessage.push(NoisePublicKey.deserialize(payload.subarray(i, i + pkLen))); handshakeMessage.push(NoisePublicKey.deserialize(payload.subarray(i, i + pkLen)));
i += pkLen; i += pkLen;
written += pkLen; written += pkLen;
// If the key is encrypted, we only read the encrypted X coordinate and the authorization tag, and we deserialize into a Noise Public Key // If the key is encrypted, we only read the encrypted X coordinate and the authorization tag, and we deserialize into a Noise Public Key
} else if (flag === 1) { } else if (flag === 1) {
const pkLen = 1 + Curve25519KeySize + tagLen; const pkLen = 1 + keySize + tagLen;
handshakeMessage.push(NoisePublicKey.deserialize(payload.subarray(i, i + pkLen))); handshakeMessage.push(NoisePublicKey.deserialize(payload.subarray(i, i + pkLen)));
i += pkLen; i += pkLen;
written += pkLen; written += pkLen;

View File

@ -9,7 +9,8 @@ import {
NoiseSecureTransferDecoder, NoiseSecureTransferDecoder,
NoiseSecureTransferEncoder, NoiseSecureTransferEncoder,
} from "./codec"; } from "./codec";
import { commitPublicKey, generateX25519KeyPair } from "./crypto"; import { commitPublicKey } from "./crypto";
import { DH25519 } from "./dh25519";
import { Handshake } from "./handshake"; import { Handshake } from "./handshake";
import { MessageNametagBufferSize, MessageNametagLength } from "./messagenametag"; import { MessageNametagBufferSize, MessageNametagLength } from "./messagenametag";
import { NoiseHandshakePatterns } from "./patterns"; import { NoiseHandshakePatterns } from "./patterns";
@ -27,17 +28,19 @@ describe("Waku Noise Sessions", () => {
// Pairing Phase // Pairing Phase
// ========== // ==========
const dhKey = new DH25519();
const hsPattern = NoiseHandshakePatterns.Noise_WakuPairing_25519_ChaChaPoly_SHA256; const hsPattern = NoiseHandshakePatterns.Noise_WakuPairing_25519_ChaChaPoly_SHA256;
// Alice static/ephemeral key initialization and commitment // Alice static/ephemeral key initialization and commitment
const aliceStaticKey = generateX25519KeyPair(); const aliceStaticKey = dhKey.generateKeyPair();
const aliceEphemeralKey = generateX25519KeyPair(); const aliceEphemeralKey = dhKey.generateKeyPair();
const s = randomBytes(32, rng); const s = randomBytes(32, rng);
const aliceCommittedStaticKey = commitPublicKey(aliceStaticKey.publicKey, s); const aliceCommittedStaticKey = commitPublicKey(aliceStaticKey.publicKey, s);
// Bob static/ephemeral key initialization and commitment // Bob static/ephemeral key initialization and commitment
const bobStaticKey = generateX25519KeyPair(); const bobStaticKey = dhKey.generateKeyPair();
const bobEphemeralKey = generateX25519KeyPair(); const bobEphemeralKey = dhKey.generateKeyPair();
const r = randomBytes(32, rng); const r = randomBytes(32, rng);
const bobCommittedStaticKey = commitPublicKey(bobStaticKey.publicKey, r); const bobCommittedStaticKey = commitPublicKey(bobStaticKey.publicKey, r);