merge with master

This commit is contained in:
Sasha 2023-05-10 23:13:14 +02:00
commit 8a7121535a
No known key found for this signature in database
3 changed files with 120 additions and 12 deletions

View File

@ -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;
} }
} }

View File

@ -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);
}
});
}); });

View File

@ -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];