refactor: `commitPublicKey` receives a hashing function

This commit is contained in:
Richard Ramos 2023-11-20 16:40:40 -04:00 committed by richΛrd
parent 6baa061f0e
commit 3cd683c2f1
5 changed files with 59 additions and 31 deletions

View File

@ -2,23 +2,13 @@ import { ChaCha20Poly1305 } from "@stablelib/chacha20poly1305";
import { Hash } from "@stablelib/hash";
import { HKDF as hkdf } from "@stablelib/hkdf";
import { RandomSource } from "@stablelib/random";
import { hash } from "@stablelib/sha256";
import { concat as uint8ArrayConcat } from "uint8arrays/concat";
import type { bytes32 } from "./@types/basic.js";
import type { KeyPair } from "./@types/keypair.js";
/**
* Generate hash using SHA2-256
* @param data data to hash
* @returns hash digest
*/
export function hashSHA256(data: Uint8Array): Uint8Array {
return hash(data);
}
/**
* HKDF key derivation function using SHA256
* HKDF key derivation function
* @param ck chaining key
* @param ikm input key material
* @param length length of each generated key
@ -79,14 +69,24 @@ export function chaCha20Poly1305Decrypt(
return ctx.open(nonce, ciphertext, ad);
}
export function hash(hash: new () => Hash, data: Uint8Array): bytes32 {
const h = new hash();
h.update(data);
const digest = h.digest();
h.clean();
return digest;
}
/**
* Generates a random static key commitment using a public key pk for randomness r as H(pk || s)
* @param h Hash function
* @param publicKey x25519 public key
* @param r random fixed-length value
* @returns 32 byte hash
*/
export function commitPublicKey(publicKey: bytes32, r: Uint8Array): bytes32 {
return hashSHA256(uint8ArrayConcat([publicKey, r]));
export function commitPublicKey(h: new () => Hash, publicKey: bytes32, r: Uint8Array): bytes32 {
const data = uint8ArrayConcat([publicKey, r]);
return hash(h, data);
}
/**

View File

@ -1,8 +1,8 @@
import { hash } from "@stablelib/sha256";
import { concat as uint8ArrayConcat } from "uint8arrays/concat";
import { equals as uint8ArrayEquals } from "uint8arrays/equals";
import { MessageNametag } from "./@types/handshake.js";
import { hashSHA256 } from "./crypto.js";
import { writeUIntLE } from "./utils.js";
export const MessageNametagLength = 16;
@ -40,6 +40,7 @@ export class MessageNametagBuffer {
if (this.secret) {
for (let i = 0; i < this.buffer.length; i++) {
const counterBytesLE = writeUIntLE(new Uint8Array(8), this.counter, 0, 8);
// TODO: determine if this hash should be sha256, or if it should depend on the handshake pattern
const d = hashSHA256(uint8ArrayConcat([this.secret, counterBytesLE]));
this.buffer[i] = toMessageNametag(d);
this.counter++;
@ -124,3 +125,12 @@ export class MessageNametagBuffer {
}
}
}
/**
* Generate hash using SHA2-256
* @param data data to hash
* @returns hash digest
*/
function hashSHA256(data: Uint8Array): Uint8Array {
return hash(data);
}

View File

@ -1,10 +1,11 @@
import { Hash } from "@stablelib/hash";
import debug from "debug";
import { fromString as uint8ArrayFromString } from "uint8arrays";
import { concat as uint8ArrayConcat } from "uint8arrays/concat";
import { equals as uint8ArrayEquals } from "uint8arrays/equals";
import type { bytes32 } from "./@types/basic.js";
import { chaCha20Poly1305Decrypt, chaCha20Poly1305Encrypt, hashSHA256, HKDF } from "./crypto.js";
import { chaCha20Poly1305Decrypt, chaCha20Poly1305Encrypt, hash, HKDF } from "./crypto.js";
import { Nonce } from "./nonce.js";
import { HandshakePattern } from "./patterns.js";
@ -192,7 +193,7 @@ export class CipherState {
* @param name name of the noise handshake pattern to hash
* @returns sha256 digest of the protocol name
*/
function hashProtocol(name: string): Uint8Array {
function hashProtocol(h: new () => Hash, name: string): Uint8Array {
// If protocol_name is less than or equal to HASHLEN bytes in length,
// sets h equal to protocol_name with zero bytes appended to make HASHLEN bytes.
// Otherwise sets h = HASH(protocol_name).
@ -203,7 +204,7 @@ function hashProtocol(name: string): Uint8Array {
h.set(protocolName);
return h;
} else {
return hashSHA256(protocolName);
return hash(h, protocolName);
}
}
@ -217,7 +218,7 @@ export class SymmetricState {
private ck: bytes32; // chaining key
constructor(private readonly handshakePattern: HandshakePattern) {
this.h = hashProtocol(handshakePattern.name);
this.h = hashProtocol(handshakePattern.hash, handshakePattern.name);
this.ck = this.h;
this.cs = new CipherState();
}
@ -269,7 +270,7 @@ export class SymmetricState {
*/
mixHash(data: Uint8Array): void {
// We hash the previous handshake hash and input data and store the result in the Symmetric State's handshake hash value
this.h = hashSHA256(uint8ArrayConcat([this.h, data]));
this.h = hash(this.handshakePattern.hash, uint8ArrayConcat([this.h, data]));
log("mixHash", this.h);
}

View File

@ -15,10 +15,9 @@ import {
NoiseSecureTransferEncoder,
} from "./codec.js";
import { commitPublicKey } from "./crypto.js";
import { DH25519 } from "./dh25519.js";
import { Handshake, HandshakeResult, HandshakeStepResult, MessageNametagError } from "./handshake.js";
import { MessageNametagLength } from "./messagenametag.js";
import { NoiseHandshakePatterns } from "./patterns.js";
import { HandshakePattern, NoiseHandshakePatterns } from "./patterns.js";
import { NoisePublicKey } from "./publickey.js";
import { QR } from "./qr.js";
@ -58,6 +57,7 @@ export class ResponderParameters {
*/
export class WakuPairing {
public readonly contentTopic: string;
private readonly hsPattern: HandshakePattern;
private initiator: boolean;
private randomFixLenVal: Uint8Array; // r or s depending on who is sending the message
@ -95,11 +95,18 @@ export class WakuPairing {
private responder: IReceiver,
private myStaticKey: KeyPair,
pairingParameters: InitiatorParameters | ResponderParameters,
private myEphemeralKey: KeyPair = new DH25519().generateKeyPair(),
private myEphemeralKey?: KeyPair,
private readonly encoderParameters: EncoderParameters = {}
) {
this.hsPattern = NoiseHandshakePatterns.Noise_WakuPairing_25519_ChaChaPoly_SHA256;
this.randomFixLenVal = randomBytes(32, rng);
this.myCommittedStaticKey = commitPublicKey(this.myStaticKey.publicKey, this.randomFixLenVal);
this.myCommittedStaticKey = commitPublicKey(this.hsPattern.hash, this.myStaticKey.publicKey, this.randomFixLenVal);
if (!this.myEphemeralKey) {
this.myEphemeralKey = NoiseHandshakePatterns.Noise_WakuPairing_25519_ChaChaPoly_SHA256.dhKey.generateKeyPair();
}
console.log(this.myEphemeralKey.publicKey);
if (pairingParameters instanceof InitiatorParameters) {
this.initiator = true;
@ -125,8 +132,8 @@ export class WakuPairing {
const preMessagePKs = [NoisePublicKey.fromPublicKey(this.qr.ephemeralKey)];
this.handshake = new Handshake({
hsPattern: NoiseHandshakePatterns.Noise_WakuPairing_25519_ChaChaPoly_SHA256,
ephemeralKey: myEphemeralKey,
hsPattern: this.hsPattern,
ephemeralKey: this.myEphemeralKey,
staticKey: myStaticKey,
prologue: this.qr.toByteArray(),
preMessagePKs,
@ -260,7 +267,11 @@ export class WakuPairing {
if (!this.handshake.hs.rs) throw new Error("invalid handshake state");
// Initiator further checks if responder's commitment opens to responder's static key received
const expectedResponderCommittedStaticKey = commitPublicKey(this.handshake.hs.rs, hsStep.transportMessage);
const expectedResponderCommittedStaticKey = commitPublicKey(
this.hsPattern.hash,
this.handshake.hs.rs,
hsStep.transportMessage
);
if (!uint8ArrayEquals(expectedResponderCommittedStaticKey, this.qr.committedStaticKey)) {
throw new Error("expected committed static key does not match the responder actual committed static key");
}
@ -333,7 +344,11 @@ export class WakuPairing {
if (!this.handshake.hs.rs) throw new Error("invalid handshake state");
// The responder further checks if the initiator's commitment opens to the initiator's static key received
const expectedInitiatorCommittedStaticKey = commitPublicKey(this.handshake.hs.rs, hsStep.transportMessage);
const expectedInitiatorCommittedStaticKey = commitPublicKey(
this.hsPattern.hash,
this.handshake.hs.rs,
hsStep.transportMessage
);
if (!uint8ArrayEquals(expectedInitiatorCommittedStaticKey, initiatorCommittedStaticKey)) {
throw new Error("expected committed static key does not match the initiator actual committed static key");
}

View File

@ -1,5 +1,6 @@
import { HMACDRBG } from "@stablelib/hmac-drbg";
import { randomBytes } from "@stablelib/random";
import { SHA256 } from "@stablelib/sha256";
import { expect } from "chai";
import { equals as uint8ArrayEquals } from "uint8arrays/equals";
@ -29,6 +30,7 @@ describe("Waku Noise Sessions", () => {
// ==========
const dhKey = new DH25519();
const hash = SHA256;
const hsPattern = NoiseHandshakePatterns.Noise_WakuPairing_25519_ChaChaPoly_SHA256;
@ -36,13 +38,13 @@ describe("Waku Noise Sessions", () => {
const aliceStaticKey = dhKey.generateKeyPair();
const aliceEphemeralKey = dhKey.generateKeyPair();
const s = randomBytes(32, rng);
const aliceCommittedStaticKey = commitPublicKey(aliceStaticKey.publicKey, s);
const aliceCommittedStaticKey = commitPublicKey(hash, aliceStaticKey.publicKey, s);
// Bob static/ephemeral key initialization and commitment
const bobStaticKey = dhKey.generateKeyPair();
const bobEphemeralKey = dhKey.generateKeyPair();
const r = randomBytes(32, rng);
const bobCommittedStaticKey = commitPublicKey(bobStaticKey.publicKey, r);
const bobCommittedStaticKey = commitPublicKey(hash, bobStaticKey.publicKey, r);
// Content topic information
const applicationName = "waku-noise-sessions";
@ -175,7 +177,7 @@ describe("Waku Noise Sessions", () => {
expect(uint8ArrayEquals(aliceStep.transportMessage, sentTransportMessage));
// Alice further checks if Bob's commitment opens to Bob's static key she just received
const expectedBobCommittedStaticKey = commitPublicKey(aliceHS.hs.rs!, aliceStep.transportMessage);
const expectedBobCommittedStaticKey = commitPublicKey(hash, aliceHS.hs.rs!, aliceStep.transportMessage);
expect(uint8ArrayEquals(expectedBobCommittedStaticKey, bobCommittedStaticKey)).to.be.true;
@ -212,7 +214,7 @@ describe("Waku Noise Sessions", () => {
expect(uint8ArrayEquals(bobStep.transportMessage, sentTransportMessage));
// Bob further checks if Alice's commitment opens to Alice's static key he just received
const expectedAliceCommittedStaticKey = commitPublicKey(bobHS.hs.rs!, bobStep.transportMessage);
const expectedAliceCommittedStaticKey = commitPublicKey(hash, bobHS.hs.rs!, bobStep.transportMessage);
expect(uint8ArrayEquals(expectedAliceCommittedStaticKey, aliceCommittedStaticKey)).to.be.true;