mirror of https://github.com/waku-org/js-noise.git
merge with master
This commit is contained in:
commit
8a7121535a
24
src/codec.ts
24
src/codec.ts
|
@ -1,5 +1,5 @@
|
||||||
import { DecodedMessage } from "@waku/core/lib/message/version_0";
|
import { DecodedMessage } from "@waku/core/lib/message/version_0";
|
||||||
import type { IDecodedMessage, IDecoder, IEncoder, IMessage, IProtoMessage } from "@waku/interfaces";
|
import type { IDecodedMessage, IDecoder, IEncoder, IMessage, IMetaSetter, IProtoMessage } from "@waku/interfaces";
|
||||||
import { WakuMessage } from "@waku/proto";
|
import { WakuMessage } from "@waku/proto";
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
|
|
||||||
|
@ -120,10 +120,17 @@ export class NoiseSecureMessage extends DecodedMessage implements IDecodedMessag
|
||||||
*/
|
*/
|
||||||
export class NoiseSecureTransferEncoder implements IEncoder {
|
export class NoiseSecureTransferEncoder implements IEncoder {
|
||||||
/**
|
/**
|
||||||
* @param contentTopic content topic on which the encoded WakuMessages were sent
|
* @param contentTopic content topic on which the encoded WakuMessages were sent.
|
||||||
* @param hsResult handshake result obtained after the handshake is successful
|
* @param hsResult handshake result obtained after the handshake is successful.
|
||||||
|
* @param ephemeral whether messages should be tagged as ephemeral defaults to true.
|
||||||
|
* @param metaSetter callback function that set the `meta` field.
|
||||||
*/
|
*/
|
||||||
constructor(public contentTopic: string, private hsResult: HandshakeResult, public ephemeral: boolean = true) {}
|
constructor(
|
||||||
|
public contentTopic: string,
|
||||||
|
private hsResult: HandshakeResult,
|
||||||
|
public ephemeral: boolean = true,
|
||||||
|
public metaSetter?: IMetaSetter
|
||||||
|
) {}
|
||||||
|
|
||||||
async toWire(message: IMessage): Promise<Uint8Array | undefined> {
|
async toWire(message: IMessage): Promise<Uint8Array | undefined> {
|
||||||
const protoMessage = await this.toProtoObj(message);
|
const protoMessage = await this.toProtoObj(message);
|
||||||
|
@ -142,7 +149,7 @@ export class NoiseSecureTransferEncoder implements IEncoder {
|
||||||
|
|
||||||
const payload = preparedPayload.serialize();
|
const payload = preparedPayload.serialize();
|
||||||
|
|
||||||
return {
|
const protoMessage = {
|
||||||
payload,
|
payload,
|
||||||
rateLimitProof: undefined,
|
rateLimitProof: undefined,
|
||||||
ephemeral: this.ephemeral,
|
ephemeral: this.ephemeral,
|
||||||
|
@ -151,6 +158,13 @@ export class NoiseSecureTransferEncoder implements IEncoder {
|
||||||
contentTopic: this.contentTopic,
|
contentTopic: this.contentTopic,
|
||||||
timestamp: BigInt(timestamp.valueOf()) * OneMillion,
|
timestamp: BigInt(timestamp.valueOf()) * OneMillion,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.metaSetter) {
|
||||||
|
const meta = this.metaSetter(protoMessage);
|
||||||
|
return { ...protoMessage, meta };
|
||||||
|
}
|
||||||
|
|
||||||
|
return protoMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { HMACDRBG } from "@stablelib/hmac-drbg";
|
import { HMACDRBG } from "@stablelib/hmac-drbg";
|
||||||
import { randomBytes } from "@stablelib/random";
|
import { randomBytes } from "@stablelib/random";
|
||||||
import type { IDecoder, IEncoder, IMessage, ISender } from "@waku/interfaces";
|
import type { IDecoder, IEncoder, IMessage, IProtoMessage, ISender } from "@waku/interfaces";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { EventEmitter } from "eventemitter3";
|
import { EventEmitter } from "eventemitter3";
|
||||||
import { pEvent } from "p-event";
|
import { pEvent } from "p-event";
|
||||||
|
@ -13,6 +13,15 @@ import { ResponderParameters, WakuPairing } from "./pairing";
|
||||||
|
|
||||||
const PUBSUB_TOPIC = "default";
|
const PUBSUB_TOPIC = "default";
|
||||||
|
|
||||||
|
const EMPTY_PROTOMESSAGE = {
|
||||||
|
timestamp: undefined,
|
||||||
|
contentTopic: "",
|
||||||
|
ephemeral: undefined,
|
||||||
|
meta: undefined,
|
||||||
|
rateLimitProof: undefined,
|
||||||
|
version: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
describe("js-noise: pairing object", () => {
|
describe("js-noise: pairing object", () => {
|
||||||
const rng = new HMACDRBG();
|
const rng = new HMACDRBG();
|
||||||
|
|
||||||
|
@ -117,4 +126,75 @@ describe("js-noise: pairing object", () => {
|
||||||
expect(message).to.be.equals("pairing has timed out");
|
expect(message).to.be.equals("pairing has timed out");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("pairs and `meta` field is encoded", async function () {
|
||||||
|
const bobStaticKey = generateX25519KeyPair();
|
||||||
|
const aliceStaticKey = generateX25519KeyPair();
|
||||||
|
|
||||||
|
// Encode the length of the payload
|
||||||
|
// Not a relevant real life example
|
||||||
|
const metaSetter = (msg: IProtoMessage & { meta: undefined }): Uint8Array => {
|
||||||
|
const buffer = new ArrayBuffer(4);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
view.setUint32(0, msg.payload.length, false);
|
||||||
|
return new Uint8Array(buffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
const recvParameters = new ResponderParameters();
|
||||||
|
const bobPairingObj = new WakuPairing(sender, responder, bobStaticKey, recvParameters, undefined, { metaSetter });
|
||||||
|
const bobExecP1 = bobPairingObj.execute();
|
||||||
|
|
||||||
|
// Confirmation is done by manually
|
||||||
|
confirmAuthCodeFlow(bobPairingObj, true);
|
||||||
|
|
||||||
|
const initParameters = bobPairingObj.getPairingInfo();
|
||||||
|
const alicePairingObj = new WakuPairing(sender, responder, aliceStaticKey, initParameters, undefined, {
|
||||||
|
metaSetter,
|
||||||
|
});
|
||||||
|
const aliceExecP1 = alicePairingObj.execute();
|
||||||
|
|
||||||
|
// Confirmation is done manually
|
||||||
|
confirmAuthCodeFlow(alicePairingObj, true);
|
||||||
|
|
||||||
|
const [bobCodecs, aliceCodecs] = await Promise.all([bobExecP1, aliceExecP1]);
|
||||||
|
|
||||||
|
const bobEncoder = bobCodecs[0];
|
||||||
|
const bobDecoder = bobCodecs[1];
|
||||||
|
const aliceEncoder = aliceCodecs[0];
|
||||||
|
const aliceDecoder = aliceCodecs[1];
|
||||||
|
|
||||||
|
// We test read/write of random messages exchanged between Alice and Bob
|
||||||
|
// Note that we exchange more than the number of messages contained in the nametag buffer to test if they are filled correctly as the communication proceeds
|
||||||
|
for (let i = 0; i < 10 * MessageNametagBufferSize; i++) {
|
||||||
|
// Alice writes to Bob
|
||||||
|
let message = randomBytes(32, rng);
|
||||||
|
let encodedMsg = await aliceEncoder.toWire({ payload: message });
|
||||||
|
let readMessageProto = await bobDecoder.fromWireToProtoObj(encodedMsg!);
|
||||||
|
let readMessage = await bobDecoder.fromProtoObj(PUBSUB_TOPIC, readMessageProto!);
|
||||||
|
|
||||||
|
expect(uint8ArrayEquals(message, readMessage!.payload)).to.be.true;
|
||||||
|
|
||||||
|
let expectedMeta = metaSetter({
|
||||||
|
...EMPTY_PROTOMESSAGE,
|
||||||
|
payload: readMessageProto!.payload,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(readMessage!.meta).to.deep.eq(expectedMeta);
|
||||||
|
|
||||||
|
// Bob writes to Alice
|
||||||
|
message = randomBytes(32, rng);
|
||||||
|
encodedMsg = await bobEncoder.toWire({ payload: message });
|
||||||
|
readMessageProto = await aliceDecoder.fromWireToProtoObj(encodedMsg!);
|
||||||
|
readMessage = await aliceDecoder.fromProtoObj(PUBSUB_TOPIC, readMessageProto!);
|
||||||
|
|
||||||
|
expect(uint8ArrayEquals(message, readMessage!.payload)).to.be.true;
|
||||||
|
|
||||||
|
expectedMeta = metaSetter({
|
||||||
|
...EMPTY_PROTOMESSAGE,
|
||||||
|
payload: readMessageProto!.payload,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(readMessage!.meta).to.deep.eq(expectedMeta);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { HMACDRBG } from "@stablelib/hmac-drbg";
|
import { HMACDRBG } from "@stablelib/hmac-drbg";
|
||||||
import { randomBytes } from "@stablelib/random";
|
import { randomBytes } from "@stablelib/random";
|
||||||
import type { ISender, IReceiver } from "@waku/interfaces";
|
import type { ISender, IMetaSetter, IReceiver } from "@waku/interfaces";
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
import { EventEmitter } from "eventemitter3";
|
import { EventEmitter } from "eventemitter3";
|
||||||
import { pEvent } from "p-event";
|
import { pEvent } from "p-event";
|
||||||
|
@ -29,6 +29,11 @@ function delay(ms: number): Promise<void> {
|
||||||
|
|
||||||
const rng = new HMACDRBG();
|
const rng = new HMACDRBG();
|
||||||
|
|
||||||
|
export interface EncoderParameters {
|
||||||
|
ephemeral?: boolean;
|
||||||
|
metaSetter?: IMetaSetter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiator parameters used to setup the pairing object
|
* Initiator parameters used to setup the pairing object
|
||||||
*/
|
*/
|
||||||
|
@ -82,13 +87,15 @@ export class WakuPairing {
|
||||||
* @param myStaticKey x25519 keypair
|
* @param myStaticKey x25519 keypair
|
||||||
* @param pairingParameters Pairing parameters (depending if this is the initiator or responder)
|
* @param pairingParameters Pairing parameters (depending if this is the initiator or responder)
|
||||||
* @param myEphemeralKey optional ephemeral key
|
* @param myEphemeralKey optional ephemeral key
|
||||||
|
* @param encoderParameters optional parameters for the resulting encoders
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private sender: ISender,
|
private sender: ISender,
|
||||||
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 = generateX25519KeyPair(),
|
||||||
|
private readonly encoderParameters: EncoderParameters = {}
|
||||||
) {
|
) {
|
||||||
this.randomFixLenVal = randomBytes(32, rng);
|
this.randomFixLenVal = randomBytes(32, rng);
|
||||||
this.myCommittedStaticKey = commitPublicKey(this.myStaticKey.publicKey, this.randomFixLenVal);
|
this.myCommittedStaticKey = commitPublicKey(this.myStaticKey.publicKey, this.randomFixLenVal);
|
||||||
|
@ -272,7 +279,7 @@ export class WakuPairing {
|
||||||
|
|
||||||
this.eventEmitter.emit("pairingComplete");
|
this.eventEmitter.emit("pairingComplete");
|
||||||
|
|
||||||
return WakuPairing.getSecureCodec(this.contentTopic, this.handshakeResult);
|
return WakuPairing.getSecureCodec(this.contentTopic, this.handshakeResult, this.encoderParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async responderHandshake(): Promise<[NoiseSecureTransferEncoder, NoiseSecureTransferDecoder]> {
|
private async responderHandshake(): Promise<[NoiseSecureTransferEncoder, NoiseSecureTransferDecoder]> {
|
||||||
|
@ -329,7 +336,7 @@ export class WakuPairing {
|
||||||
|
|
||||||
this.eventEmitter.emit("pairingComplete");
|
this.eventEmitter.emit("pairingComplete");
|
||||||
|
|
||||||
return WakuPairing.getSecureCodec(this.contentTopic, this.handshakeResult);
|
return WakuPairing.getSecureCodec(this.contentTopic, this.handshakeResult, this.encoderParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -337,13 +344,20 @@ export class WakuPairing {
|
||||||
* to continue a session using a stored hsResult
|
* to continue a session using a stored hsResult
|
||||||
* @param contentTopic Content topic for the waku messages
|
* @param contentTopic Content topic for the waku messages
|
||||||
* @param hsResult Noise Pairing result
|
* @param hsResult Noise Pairing result
|
||||||
|
* @param encoderParameters Parameters for the resulting encoder
|
||||||
* @returns an array with [NoiseSecureTransferEncoder, NoiseSecureTransferDecoder]
|
* @returns an array with [NoiseSecureTransferEncoder, NoiseSecureTransferDecoder]
|
||||||
*/
|
*/
|
||||||
static getSecureCodec(
|
static getSecureCodec(
|
||||||
contentTopic: string,
|
contentTopic: string,
|
||||||
hsResult: HandshakeResult
|
hsResult: HandshakeResult,
|
||||||
|
encoderParameters: EncoderParameters
|
||||||
): [NoiseSecureTransferEncoder, NoiseSecureTransferDecoder] {
|
): [NoiseSecureTransferEncoder, NoiseSecureTransferDecoder] {
|
||||||
const secureEncoder = new NoiseSecureTransferEncoder(contentTopic, hsResult);
|
const secureEncoder = new NoiseSecureTransferEncoder(
|
||||||
|
contentTopic,
|
||||||
|
hsResult,
|
||||||
|
encoderParameters.ephemeral,
|
||||||
|
encoderParameters.metaSetter
|
||||||
|
);
|
||||||
const secureDecoder = new NoiseSecureTransferDecoder(contentTopic, hsResult);
|
const secureDecoder = new NoiseSecureTransferDecoder(contentTopic, hsResult);
|
||||||
|
|
||||||
return [secureEncoder, secureDecoder];
|
return [secureEncoder, secureDecoder];
|
||||||
|
|
Loading…
Reference in New Issue