mirror of
https://github.com/logos-messaging/js-noise.git
synced 2026-01-08 00:23:13 +00:00
handle secure transfer
This commit is contained in:
parent
39f54c2c15
commit
aafdf61572
57
src/codec.ts
57
src/codec.ts
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
*/
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user