From bf665a0222cf1728a23feb63fd12f58eb1fbe501 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Mon, 20 Nov 2023 14:34:08 -0400 Subject: [PATCH] refactor: SHA256 as parameter --- src/crypto.ts | 13 ++++++++++--- src/handshake.ts | 2 +- src/handshake_state.ts | 13 ++++--------- src/noise.ts | 15 +++++++-------- src/patterns.ts | 9 +++++++++ 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/crypto.ts b/src/crypto.ts index cd7fc28..215b07f 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -1,6 +1,7 @@ import { ChaCha20Poly1305, TAG_LENGTH } from "@stablelib/chacha20poly1305"; +import { Hash } from "@stablelib/hash"; import { HKDF as hkdf } from "@stablelib/hkdf"; -import { hash, SHA256 } from "@stablelib/sha256"; +import { hash } from "@stablelib/sha256"; import * as x25519 from "@stablelib/x25519"; import { concat as uint8ArrayConcat } from "uint8arrays/concat"; @@ -43,9 +44,15 @@ export function intoCurve25519Key(s: Uint8Array): bytes32 { * @param numKeys number of keys to generate * @returns array of `numValues` length containing Uint8Array keys of a given byte `length` */ -export function HKDF(ck: bytes32, ikm: Uint8Array, length: number, numKeys: number): Array { +export function HKDF( + hash: new () => Hash, + ck: bytes32, + ikm: Uint8Array, + length: number, + numKeys: number +): Array { const numBytes = length * numKeys; - const okm = new hkdf(SHA256, ikm, ck).expand(numBytes); + const okm = new hkdf(hash, ikm, ck).expand(numBytes); const result = []; for (let i = 0; i < numBytes; i += length) { const k = okm.subarray(i, i + length); diff --git a/src/handshake.ts b/src/handshake.ts index 8dfc6a2..6a9623c 100644 --- a/src/handshake.ts +++ b/src/handshake.ts @@ -173,7 +173,7 @@ export class Handshake { // Generates an 8 decimal digits authorization code using HKDF and the handshake state genAuthcode(): string { - const [output0] = HKDF(this.hs.ss.h, new Uint8Array(), 8, 1); + const [output0] = HKDF(this.hs.handshakePattern.hash, this.hs.ss.h, new Uint8Array(), 8, 1); const bn = new BN(output0); const code = bn.mod(new BN(100_000_000)).toString().padStart(8, "0"); return code.toString(); diff --git a/src/handshake_state.ts b/src/handshake_state.ts index 570d799..0f9f2e8 100644 --- a/src/handshake_state.ts +++ b/src/handshake_state.ts @@ -31,19 +31,14 @@ export class HandshakeState { re?: bytes32; ss: SymmetricState; initiator: boolean; - handshakePattern: HandshakePattern; msgPatternIdx: number; - psk: Uint8Array; - constructor(hsPattern: HandshakePattern, psk: Uint8Array) { + constructor(public readonly handshakePattern: HandshakePattern, public psk: Uint8Array) { // By default the Handshake State initiator flag is set to false // Will be set to true when the user associated to the handshake state starts an handshake this.initiator = false; - this.handshakePattern = hsPattern; - this.psk = psk; - - this.ss = new SymmetricState(hsPattern); + this.ss = new SymmetricState(handshakePattern); this.msgPatternIdx = 0; } @@ -101,14 +96,14 @@ export class HandshakeState { } genMessageNametagSecrets(): { nms1: Uint8Array; nms2: Uint8Array } { - const [nms1, nms2] = HKDF(this.ss.h, new Uint8Array(), 2, 32); + const [nms1, nms2] = HKDF(this.handshakePattern.hash, this.ss.h, new Uint8Array(), 2, 32); return { nms1, nms2 }; } // Uses the cryptographic information stored in the input handshake state to generate a random message nametag // In current implementation the messageNametag = HKDF(handshake hash value), but other derivation mechanisms can be implemented toMessageNametag(): MessageNametag { - const [output] = HKDF(this.ss.h, new Uint8Array(), 32, 1); + const [output] = HKDF(this.handshakePattern.hash, this.ss.h, new Uint8Array(), 32, 1); return output.subarray(0, MessageNametagLength); } diff --git a/src/noise.ts b/src/noise.ts index 9be9e08..4ea1f72 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -216,11 +216,10 @@ export class SymmetricState { h: bytes32; // handshake hash private ck: bytes32; // chaining key - constructor(private readonly hsPattern: HandshakePattern) { - this.h = hashProtocol(hsPattern.name); + constructor(private readonly handshakePattern: HandshakePattern) { + this.h = hashProtocol(handshakePattern.name); this.ck = this.h; this.cs = new CipherState(); - this.hsPattern = hsPattern; } /** @@ -233,7 +232,7 @@ export class SymmetricState { this.cs.equals(other.cs) && uint8ArrayEquals(this.ck, other.ck) && uint8ArrayEquals(this.h, other.h) && - this.hsPattern.equals(other.hsPattern) + this.handshakePattern.equals(other.handshakePattern) ); } @@ -242,7 +241,7 @@ export class SymmetricState { * @returns a copy of the SymmetricState */ clone(): SymmetricState { - const ss = new SymmetricState(this.hsPattern); + const ss = new SymmetricState(this.handshakePattern); ss.cs = this.cs.clone(); ss.ck = new Uint8Array(this.ck); ss.h = new Uint8Array(this.h); @@ -256,7 +255,7 @@ export class SymmetricState { */ mixKey(inputKeyMaterial: Uint8Array): void { // We derive two keys using HKDF - const [ck, tempK] = HKDF(this.ck, inputKeyMaterial, 32, 2); + const [ck, tempK] = HKDF(this.handshakePattern.hash, this.ck, inputKeyMaterial, 32, 2); // We update ck and the Cipher state's key k using the output of HDKF this.cs = new CipherState(tempK); this.ck = ck; @@ -281,7 +280,7 @@ export class SymmetricState { */ mixKeyAndHash(inputKeyMaterial: Uint8Array): void { // Derives 3 keys using HKDF, the chaining key and the input key material - const [tmpKey0, tmpKey1, tmpKey2] = HKDF(this.ck, inputKeyMaterial, 32, 3); + const [tmpKey0, tmpKey1, tmpKey2] = HKDF(this.handshakePattern.hash, this.ck, inputKeyMaterial, 32, 3); // Sets the chaining key this.ck = tmpKey0; // Updates the handshake hash value @@ -334,7 +333,7 @@ export class SymmetricState { */ split(): { cs1: CipherState; cs2: CipherState } { // Derives 2 keys using HKDF and the chaining key - const [tmpKey1, tmpKey2] = HKDF(this.ck, new Uint8Array(0), 32, 2); + const [tmpKey1, tmpKey2] = HKDF(this.handshakePattern.hash, this.ck, new Uint8Array(0), 32, 2); // Returns a tuple of two Cipher States initialized with the derived keys return { cs1: new CipherState(tmpKey1), diff --git a/src/patterns.ts b/src/patterns.ts index cd1aa05..5e11a47 100644 --- a/src/patterns.ts +++ b/src/patterns.ts @@ -1,3 +1,6 @@ +import { Hash } from "@stablelib/hash"; +import { SHA256 } from "@stablelib/sha256"; + /** * The Noise tokens appearing in Noise (pre)message patterns * as in http://www.noiseprotocol.org/noise.html#handshake-pattern-basics @@ -70,6 +73,7 @@ export class MessagePattern { export class HandshakePattern { constructor( public readonly name: string, + public readonly hash: new () => Hash, public readonly preMessagePatterns: Array, public readonly messagePatterns: Array ) {} @@ -100,6 +104,7 @@ export class HandshakePattern { export const NoiseHandshakePatterns = { K1K1: new HandshakePattern( "Noise_K1K1_25519_ChaChaPoly_SHA256", + SHA256, [ new PreMessagePattern(MessageDirection.r, [NoiseTokens.s]), new PreMessagePattern(MessageDirection.l, [NoiseTokens.s]), @@ -112,6 +117,7 @@ export const NoiseHandshakePatterns = { ), XK1: new HandshakePattern( "Noise_XK1_25519_ChaChaPoly_SHA256", + SHA256, [new PreMessagePattern(MessageDirection.l, [NoiseTokens.s])], [ new MessagePattern(MessageDirection.r, [NoiseTokens.e]), @@ -121,6 +127,7 @@ export const NoiseHandshakePatterns = { ), XX: new HandshakePattern( "Noise_XX_25519_ChaChaPoly_SHA256", + SHA256, [], [ new MessagePattern(MessageDirection.r, [NoiseTokens.e]), @@ -130,6 +137,7 @@ export const NoiseHandshakePatterns = { ), XXpsk0: new HandshakePattern( "Noise_XXpsk0_25519_ChaChaPoly_SHA256", + SHA256, [], [ new MessagePattern(MessageDirection.r, [NoiseTokens.psk, NoiseTokens.e]), @@ -139,6 +147,7 @@ export const NoiseHandshakePatterns = { ), WakuPairing: new HandshakePattern( "Noise_WakuPairing_25519_ChaChaPoly_SHA256", + SHA256, [new PreMessagePattern(MessageDirection.l, [NoiseTokens.e])], [ new MessagePattern(MessageDirection.r, [NoiseTokens.e, NoiseTokens.ee]),