mirror of https://github.com/waku-org/js-noise.git
refactor: `commitPublicKey` receives a hashing function
This commit is contained in:
parent
6baa061f0e
commit
3cd683c2f1
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
11
src/noise.ts
11
src/noise.ts
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue