handle secure transfer

This commit is contained in:
Richard Ramos 2022-11-22 10:13:57 -04:00
parent 39f54c2c15
commit aafdf61572
No known key found for this signature in database
GPG Key ID: BD36D48BC9FFC88C
4 changed files with 137 additions and 17 deletions

View File

@ -32,13 +32,26 @@ export class NoiseHandshakeEncoder implements Encoder {
}
}
export class MessageV2 extends MessageV0 implements Message {
export class NoiseHandshakeMessage extends MessageV0 implements Message {
get payloadV2(): PayloadV2 {
return PayloadV2.deserialize(this.payload!);
}
}
export class NoiseHandshakeDecoder implements Decoder<MessageV2> {
export class NoiseSecureMessage extends MessageV0 implements Message {
private readonly _decodedPayload: Uint8Array;
constructor(proto: proto_message.WakuMessage, decodedPayload: Uint8Array) {
super(proto);
this._decodedPayload = decodedPayload;
}
get payload(): Uint8Array {
return this._decodedPayload;
}
}
export class NoiseHandshakeDecoder implements Decoder<NoiseHandshakeMessage> {
constructor(public contentTopic: string) {}
decodeProto(bytes: Uint8Array): Promise<ProtoMessage | undefined> {
@ -47,7 +60,7 @@ export class NoiseHandshakeDecoder implements Decoder<MessageV2> {
return Promise.resolve(protoMessage);
}
async decode(proto: ProtoMessage): Promise<MessageV2 | undefined> {
async decode(proto: ProtoMessage): Promise<NoiseHandshakeMessage | undefined> {
// https://github.com/status-im/js-waku/issues/921
if (proto.version === undefined) {
proto.version = 0;
@ -63,7 +76,7 @@ export class NoiseHandshakeDecoder implements Decoder<MessageV2> {
return;
}
return new MessageV2(proto);
return new NoiseHandshakeMessage(proto);
}
}
@ -82,6 +95,7 @@ export class NoiseSecureTransferEncoder implements Encoder {
log("No payload to encrypt, skipping: ", message);
return;
}
const preparedPayload = this.hsResult.writeMessage(message.payload, this.hsResult.nametagsOutbound);
const payload = preparedPayload.serialize();
@ -95,6 +109,35 @@ export class NoiseSecureTransferEncoder implements Encoder {
}
}
/*
export class NoiseSecureTransferDecoder implements Decoder<> {}
*/
export class NoiseSecureTransferDecoder implements Decoder<NoiseSecureMessage> {
constructor(public contentTopic: string, private hsResult: HandshakeResult) {}
decodeProto(bytes: Uint8Array): Promise<ProtoMessage | undefined> {
const protoMessage = proto_message.WakuMessage.decode(bytes);
log("Message decoded", protoMessage);
return Promise.resolve(protoMessage);
}
async decode(proto: ProtoMessage): Promise<NoiseSecureMessage | undefined> {
// https://github.com/status-im/js-waku/issues/921
if (proto.version === undefined) {
proto.version = 0;
}
if (proto.version !== Version) {
log("Failed to decode due to incorrect version, expected:", Version, ", actual:", proto.version);
return Promise.resolve(undefined);
}
if (!proto.payload) {
log("No payload, skipping: ", proto);
return;
}
const payloadV2 = PayloadV2.deserialize(proto.payload);
const decryptedPayload = this.hsResult.readMessage(payloadV2, this.hsResult.nametagsInbound);
return new NoiseSecureMessage(proto, decryptedPayload);
}
}

View File

@ -272,8 +272,8 @@ export class Handshake {
hsResult.nametagsOutbound.initNametagsBuffer();
// We store the optional fields rs and h
hsResult.rs = this.hs.rs!;
hsResult.h = this.hs.ss.h;
hsResult.rs = new Uint8Array(this.hs.rs!);
hsResult.h = new Uint8Array(this.hs.ss.h);
return hsResult;
}

View File

@ -23,6 +23,12 @@ export class MessageNametagBuffer {
counter = 0;
secret?: Uint8Array;
constructor() {
for (let i = 0; i < this.buffer.length; i++) {
this.buffer[i] = new Uint8Array(MessageNametagLength);
}
}
// Initializes the empty Message nametag buffer. The n-th nametag is equal to HKDF( secret || n )
initNametagsBuffer(): void {
// We default the counter and buffer fields
@ -44,7 +50,7 @@ export class MessageNametagBuffer {
pop(): MessageNametag {
// Note that if the input MessageNametagBuffer is set to default, an all 0 messageNametag is returned
const messageNametag = this.buffer[0];
const messageNametag = new Uint8Array(this.buffer[0]);
this.delete(1);
return messageNametag;
}

View File

@ -3,11 +3,16 @@ import { randomBytes } from "@stablelib/random";
import { expect } from "chai";
import { equals as uint8ArrayEquals } from "uint8arrays/equals";
import { NoiseHandshakeDecoder, NoiseHandshakeEncoder } from "./codec";
import {
NoiseHandshakeDecoder,
NoiseHandshakeEncoder,
NoiseSecureTransferDecoder,
NoiseSecureTransferEncoder,
} from "./codec";
import { commitPublicKey, generateX25519KeyPair } from "./crypto";
import { Handshake } from "./handshake";
import { NoiseHandshakePatterns } from "./patterns";
import { MessageNametagLength } from "./payload";
import { MessageNametagBufferSize, MessageNametagLength } from "./payload";
import { NoisePublicKey } from "./publickey";
import { fromQr, toQr } from "./utils";
@ -211,15 +216,81 @@ describe("Waku Noise Sessions", () => {
// Secure Transfer Phase
// ==========
// We finalize the handshake to retrieve the Inbound/Outbound Symmetric States
const aliceHSResult = aliceHS.finalizeHandshake();
const bobHSResult = bobHS.finalizeHandshake();
const aliceEncoder = new NoiseSecureTransferEncoder(contentTopic, aliceHSResult);
const bobEncoder = new NoiseSecureTransferEncoder(contentTopic, bobHSResult);
const aliceDecoder = new NoiseSecureTransferDecoder(contentTopic, aliceHSResult);
const bobDecoder = new NoiseSecureTransferDecoder(contentTopic, bobHSResult);
// 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.encode({ payload: message });
let readMessageProto = await bobDecoder.decodeProto(encodedMsg!);
let readMessage = await bobDecoder.decode(readMessageProto!);
expect(uint8ArrayEquals(message, readMessage!.payload)).to.be.true;
// Bob writes to Alice
message = randomBytes(32, rng);
encodedMsg = await bobEncoder.encode({ payload: message });
readMessageProto = await aliceDecoder.decodeProto(encodedMsg!);
readMessage = await aliceDecoder.decode(readMessageProto!);
expect(uint8ArrayEquals(message, readMessage!.payload)).to.be.true;
}
// TODO
// TODO
// TODO
// TODO
// TODO
// TODO
// TODO
// TODO
// TODO
// TODO
/*
// We test how nametag buffers help in detecting lost messages
// Alice writes two messages to Bob, but only the second is received
try {
const message = randomBytes(32, rng);
payload2 = aliceHSResult.writeMessage(message, aliceHSResult.nametagsOutbound);
message = randomBytes(32, rng);
payload2 = aliceHSResult.writeMessage(aliceHSResult.nametagsOutbound);
} catch (NoiseSomeMessagesWereLost) {
let readMessage = readMessage(
bobHSResult,
payload2,
(inboundMessageNametagBuffer = bobHSResult.nametagsInbound)
).get();
}
// We adjust bob nametag buffer for next test (i.e. the missed message is correctly recovered)
bobHSResult.nametagsInbound.delete(2);
let message = randomBytes(32, rng);
payload2 = writeMessage(bobHSResult, message, (outboundMessageNametagBuffer = bobHSResult.nametagsOutbound));
readMessage = readMessage(
aliceHSResult,
payload2,
(inboundMessageNametagBuffer = aliceHSResult.nametagsInbound)
).get();
expect(uint8ArrayEquals(message, readMessage!.payload)).to.be.true;
// We test if a missing nametag is correctly detected
try {
const message = randomBytes(32, rng);
const payload2 = aliceHSResult.writeMessage(message, aliceHSResult.nametagsOutbound);
bobHSResult.nametagsInbound.delete(1);
} catch (NoiseMessageNametagError) {
let readMessage = readMessage(
bobHSResult,
payload2,
(inboundMessageNametagBuffer = bobHSResult.nametagsInbound)
).get();
}
*/
});
});