feat: enable meta field to be set

Ref: https://github.com/waku-org/js-waku/issues/1208
This commit is contained in:
fryorcraken.eth 2023-05-09 16:27:00 +10:00
parent 174bc32617
commit 4f18b886c9
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
4 changed files with 122 additions and 14 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "@waku/noise",
"version": "0.0.2",
"version": "0.0.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@waku/noise",
"version": "0.0.2",
"version": "0.0.3",
"license": "Apache-2.0 OR MIT",
"dependencies": {
"@stablelib/chacha20poly1305": "^1.0.1",

View File

@ -1,5 +1,5 @@
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 debug from "debug";
@ -120,10 +120,17 @@ export class NoiseSecureMessage extends DecodedMessage implements IDecodedMessag
*/
export class NoiseSecureTransferEncoder implements IEncoder {
/**
* @param contentTopic content topic on which the encoded WakuMessages were sent
* @param hsResult handshake result obtained after the handshake is successful
* @param contentTopic content topic on which the encoded WakuMessages were sent.
* @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> {
const protoMessage = await this.toProtoObj(message);
@ -142,7 +149,7 @@ export class NoiseSecureTransferEncoder implements IEncoder {
const payload = preparedPayload.serialize();
return {
const protoMessage = {
payload,
rateLimitProof: undefined,
ephemeral: this.ephemeral,
@ -151,6 +158,13 @@ export class NoiseSecureTransferEncoder implements IEncoder {
contentTopic: this.contentTopic,
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 { 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 { EventEmitter } from "eventemitter3";
import { pEvent } from "p-event";
@ -13,6 +13,15 @@ import { ResponderParameters, WakuPairing } from "./pairing";
const PUBSUB_TOPIC = "default";
const EMPTY_PROTOMESSAGE = {
timestamp: undefined,
contentTopic: "",
ephemeral: undefined,
meta: undefined,
rateLimitProof: undefined,
version: undefined,
};
describe("js-noise: pairing object", () => {
const rng = new HMACDRBG();
@ -117,4 +126,75 @@ describe("js-noise: pairing object", () => {
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 { randomBytes } from "@stablelib/random";
import type { IDecoder, ISender } from "@waku/interfaces";
import type { IDecoder, IMetaSetter, ISender } from "@waku/interfaces";
import debug from "debug";
import { EventEmitter } from "eventemitter3";
import { pEvent } from "p-event";
@ -55,6 +55,11 @@ function delay(ms: number): Promise<void> {
const rng = new HMACDRBG();
export interface EncoderParameters {
ephemeral?: boolean;
metaSetter?: IMetaSetter;
}
/**
* Initiator parameters used to setup the pairing object
*/
@ -108,13 +113,15 @@ export class WakuPairing {
* @param myStaticKey x25519 keypair
* @param pairingParameters Pairing parameters (depending if this is the initiator or responder)
* @param myEphemeralKey optional ephemeral key
* @param encoderParameters optional parameters for the resulting encoders
*/
constructor(
private sender: ISender,
private responder: Responder,
private myStaticKey: KeyPair,
pairingParameters: InitiatorParameters | ResponderParameters,
private myEphemeralKey: KeyPair = generateX25519KeyPair()
private myEphemeralKey: KeyPair = generateX25519KeyPair(),
private readonly encoderParameters: EncoderParameters = {}
) {
this.randomFixLenVal = randomBytes(32, rng);
this.myCommittedStaticKey = commitPublicKey(this.myStaticKey.publicKey, this.randomFixLenVal);
@ -293,7 +300,7 @@ export class WakuPairing {
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]> {
@ -350,7 +357,7 @@ export class WakuPairing {
this.eventEmitter.emit("pairingComplete");
return WakuPairing.getSecureCodec(this.contentTopic, this.handshakeResult);
return WakuPairing.getSecureCodec(this.contentTopic, this.handshakeResult, this.encoderParameters);
}
/**
@ -358,13 +365,20 @@ export class WakuPairing {
* to continue a session using a stored hsResult
* @param contentTopic Content topic for the waku messages
* @param hsResult Noise Pairing result
* @param encoderParameters Parameters for the resulting encoder
* @returns an array with [NoiseSecureTransferEncoder, NoiseSecureTransferDecoder]
*/
static getSecureCodec(
contentTopic: string,
hsResult: HandshakeResult
hsResult: HandshakeResult,
encoderParameters: EncoderParameters
): [NoiseSecureTransferEncoder, NoiseSecureTransferDecoder] {
const secureEncoder = new NoiseSecureTransferEncoder(contentTopic, hsResult);
const secureEncoder = new NoiseSecureTransferEncoder(
contentTopic,
hsResult,
encoderParameters.ephemeral,
encoderParameters.metaSetter
);
const secureDecoder = new NoiseSecureTransferDecoder(contentTopic, hsResult);
return [secureEncoder, secureDecoder];