From 2bb63e3e91a030e13a0e172c73baa644ec64546f Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Fri, 3 Jan 2020 14:53:14 +0100 Subject: [PATCH 01/16] Create handshake handler files --- src/crypto.ts | 2 +- src/handshake-ik.ts | 44 +++++++++++++++++++++++++++ src/{handshake.ts => handshake-xx.ts} | 0 src/noise.ts | 8 ++--- test/handshake.test.ts | 2 +- test/noise.test.ts | 2 +- 6 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 src/handshake-ik.ts rename src/{handshake.ts => handshake-xx.ts} (100%) diff --git a/src/crypto.ts b/src/crypto.ts index 34f8c06..7ae4733 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -1,5 +1,5 @@ import { Buffer } from "buffer"; -import { Handshake } from "./handshake"; +import { Handshake } from "./handshake-xx"; interface ReturnEncryptionWrapper { (source: Iterable): AsyncIterableIterator; diff --git a/src/handshake-ik.ts b/src/handshake-ik.ts new file mode 100644 index 0000000..b38a5ee --- /dev/null +++ b/src/handshake-ik.ts @@ -0,0 +1,44 @@ +import {NoiseSession} from "./@types/handshake"; +import {bytes, bytes32} from "./@types/basic"; +import {KeyPair, PeerId} from "./@types/libp2p"; +import {WrappedConnection} from "./noise"; +import {IKHandshake} from "./handshakes/ik"; + +export class Handshake { + public isInitiator: boolean; + public session: NoiseSession; + + private libp2pPrivateKey: bytes; + private libp2pPublicKey: bytes; + private prologue: bytes32; + private staticKeys: KeyPair; + private connection: WrappedConnection; + private remotePeer: PeerId; + private ik: IKHandshake; + + constructor( + isInitiator: boolean, + libp2pPrivateKey: bytes, + libp2pPublicKey: bytes, + prologue: bytes32, + staticKeys: KeyPair, + connection: WrappedConnection, + remotePeer: PeerId, + handshake?: IKHandshake, + ) { + this.isInitiator = isInitiator; + this.libp2pPrivateKey = libp2pPrivateKey; + this.libp2pPublicKey = libp2pPublicKey; + this.prologue = prologue; + this.staticKeys = staticKeys; + this.connection = connection; + this.remotePeer = remotePeer; + + this.ik = handshake || new IKHandshake(); + + // Dummy data + // TODO: Load remote static keys if found + const remoteStaticKeys = this.staticKeys; + this.session = this.ik.initSession(this.isInitiator, this.prologue, this.staticKeys, remoteStaticKeys.publicKey); + } +} diff --git a/src/handshake.ts b/src/handshake-xx.ts similarity index 100% rename from src/handshake.ts rename to src/handshake-xx.ts diff --git a/src/noise.ts b/src/noise.ts index 068ade3..8a5f30a 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -6,7 +6,7 @@ import ensureBuffer from 'it-buffer'; import pipe from 'it-pipe'; import lp from 'it-length-prefixed'; -import { Handshake } from "./handshake"; +import { Handshake } from "./handshake-xx"; import { generateKeypair } from "./utils"; import { uint16BEDecode, uint16BEEncode } from "./encoder"; import { decryptStream, encryptStream } from "./crypto"; @@ -48,7 +48,7 @@ export class Noise implements NoiseConnection { public async secureOutbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise { const wrappedConnection = Wrap(connection); const libp2pPublicKey = localPeer.marshalPubKey(); - const handshake = await this.performHandshake(wrappedConnection, true, libp2pPublicKey, remotePeer); + const handshake = await this.performXXHandshake(wrappedConnection, true, libp2pPublicKey, remotePeer); const conn = await this.createSecureConnection(wrappedConnection, handshake); return { @@ -67,7 +67,7 @@ export class Noise implements NoiseConnection { public async secureInbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise { const wrappedConnection = Wrap(connection); const libp2pPublicKey = localPeer.marshalPubKey(); - const handshake = await this.performHandshake(wrappedConnection, false, libp2pPublicKey, remotePeer); + const handshake = await this.performXXHandshake(wrappedConnection, false, libp2pPublicKey, remotePeer); const conn = await this.createSecureConnection(wrappedConnection, handshake); return { @@ -76,7 +76,7 @@ export class Noise implements NoiseConnection { }; } - private async performHandshake( + private async performXXHandshake( connection: WrappedConnection, isInitiator: boolean, libp2pPublicKey: bytes, diff --git a/test/handshake.test.ts b/test/handshake.test.ts index 6019711..1ed6fa7 100644 --- a/test/handshake.test.ts +++ b/test/handshake.test.ts @@ -3,7 +3,7 @@ import Duplex from 'it-pair/duplex'; import {Buffer} from "buffer"; import Wrap from "it-pb-rpc"; -import {Handshake} from "../src/handshake"; +import {Handshake} from "../src/handshake-xx"; import {generateKeypair} from "../src/utils"; import {createPeerIdsFromFixtures} from "./fixtures/peer"; import {getKeyPairFromPeerId} from "./utils"; diff --git a/test/noise.test.ts b/test/noise.test.ts index 6594473..9e8ded3 100644 --- a/test/noise.test.ts +++ b/test/noise.test.ts @@ -5,7 +5,7 @@ import { Noise } from "../src"; import {createPeerIdsFromFixtures} from "./fixtures/peer"; import Wrap from "it-pb-rpc"; import { random } from "bcrypto"; -import {Handshake} from "../src/handshake"; +import {Handshake} from "../src/handshake-xx"; import { createHandshakePayload, generateKeypair, From ac12fbf987e9de912c3d499619832ef411da876d Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Fri, 3 Jan 2020 15:07:46 +0100 Subject: [PATCH 02/16] Update noise interface --- src/noise.ts | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/noise.ts b/src/noise.ts index 8a5f30a..5e94cc6 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -48,7 +48,7 @@ export class Noise implements NoiseConnection { public async secureOutbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise { const wrappedConnection = Wrap(connection); const libp2pPublicKey = localPeer.marshalPubKey(); - const handshake = await this.performXXHandshake(wrappedConnection, true, libp2pPublicKey, remotePeer); + const handshake = await this.performHandshake(wrappedConnection, true, libp2pPublicKey, remotePeer); const conn = await this.createSecureConnection(wrappedConnection, handshake); return { @@ -67,7 +67,7 @@ export class Noise implements NoiseConnection { public async secureInbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise { const wrappedConnection = Wrap(connection); const libp2pPublicKey = localPeer.marshalPubKey(); - const handshake = await this.performXXHandshake(wrappedConnection, false, libp2pPublicKey, remotePeer); + const handshake = await this.performHandshake(wrappedConnection, false, libp2pPublicKey, remotePeer); const conn = await this.createSecureConnection(wrappedConnection, handshake); return { @@ -76,6 +76,28 @@ export class Noise implements NoiseConnection { }; } + /** + * If Noise pipes supported, tries IK handshake first with XX as fallback if it fails. + * If remote peer static key is unknown, use XX. + * @param connection + * @param isInitiator + * @param libp2pPublicKey + * @param remotePeer + */ + private async performHandshake( + connection: WrappedConnection, + isInitiator: boolean, + libp2pPublicKey: bytes, + remotePeer: PeerId, + ): Promise { + if (false) { + // TODO: Implement noise pipes + + } else { + return await this.performXXHandshake(connection, isInitiator, libp2pPublicKey, remotePeer) + } + } + private async performXXHandshake( connection: WrappedConnection, isInitiator: boolean, From e797dc274149c7ed816daa562bca7d7a2130f962 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Fri, 3 Jan 2020 15:43:56 +0100 Subject: [PATCH 03/16] Create handshake interface to support performing different handshakes --- src/@types/handshake-interface.ts | 8 ++++++++ src/crypto.ts | 6 +++--- src/handshake-ik.ts | 2 +- src/handshake-xx.ts | 9 +++++---- src/noise.ts | 26 +++++++++++++------------- 5 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 src/@types/handshake-interface.ts diff --git a/src/@types/handshake-interface.ts b/src/@types/handshake-interface.ts new file mode 100644 index 0000000..5c152e5 --- /dev/null +++ b/src/@types/handshake-interface.ts @@ -0,0 +1,8 @@ +import {bytes} from "./basic"; +import {NoiseSession} from "./handshake"; + +export interface HandshakeInterface { + session: NoiseSession; + encrypt(plaintext: bytes, session: NoiseSession): bytes; + decrypt(ciphertext: bytes, session: NoiseSession): bytes; +} diff --git a/src/crypto.ts b/src/crypto.ts index 7ae4733..eff1494 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -1,5 +1,5 @@ import { Buffer } from "buffer"; -import { Handshake } from "./handshake-xx"; +import {HandshakeInterface} from "./@types/handshake-interface"; interface ReturnEncryptionWrapper { (source: Iterable): AsyncIterableIterator; @@ -8,7 +8,7 @@ interface ReturnEncryptionWrapper { const maxPlaintextLength = 65519; // Returns generator that encrypts payload from the user -export function encryptStream(handshake: Handshake): ReturnEncryptionWrapper { +export function encryptStream(handshake: HandshakeInterface): ReturnEncryptionWrapper { return async function * (source) { for await (const chunk of source) { const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length); @@ -28,7 +28,7 @@ export function encryptStream(handshake: Handshake): ReturnEncryptionWrapper { // Decrypt received payload to the user -export function decryptStream(handshake: Handshake): ReturnEncryptionWrapper { +export function decryptStream(handshake: HandshakeInterface): ReturnEncryptionWrapper { return async function * (source) { for await (const chunk of source) { const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length); diff --git a/src/handshake-ik.ts b/src/handshake-ik.ts index b38a5ee..da001bc 100644 --- a/src/handshake-ik.ts +++ b/src/handshake-ik.ts @@ -4,7 +4,7 @@ import {KeyPair, PeerId} from "./@types/libp2p"; import {WrappedConnection} from "./noise"; import {IKHandshake} from "./handshakes/ik"; -export class Handshake { +export class Handshake { // implements HandshakeHandler public isInitiator: boolean; public session: NoiseSession; diff --git a/src/handshake-xx.ts b/src/handshake-xx.ts index b2d96dc..3500077 100644 --- a/src/handshake-xx.ts +++ b/src/handshake-xx.ts @@ -4,6 +4,7 @@ import { XXHandshake } from "./handshakes/xx"; import { KeyPair, PeerId } from "./@types/libp2p"; import { bytes, bytes32 } from "./@types/basic"; import { NoiseSession } from "./@types/handshake"; +import {HandshakeInterface} from "./@types/handshake-interface"; import { createHandshakePayload, getHandshakePayload, @@ -15,7 +16,7 @@ import { logger } from "./logger"; import { decodeMessageBuffer, encodeMessageBuffer } from "./encoder"; import { WrappedConnection } from "./noise"; -export class Handshake { +export class Handshake implements HandshakeInterface { public isInitiator: boolean; public session: NoiseSession; @@ -50,7 +51,7 @@ export class Handshake { } // stage 0 - async propose(): Promise { + public async propose(): Promise { if (this.isInitiator) { logger("Stage 0 - Initiator starting to send first message."); const messageBuffer = this.xx.sendMessage(this.session, Buffer.alloc(0)); @@ -65,7 +66,7 @@ export class Handshake { } // stage 1 - async exchange(): Promise { + public async exchange(): Promise { if (this.isInitiator) { logger('Stage 1 - Initiator waiting to receive first message from responder...'); const receivedMessageBuffer = decodeMessageBuffer((await this.connection.readLP()).slice()); @@ -97,7 +98,7 @@ export class Handshake { } // stage 2 - async finish(earlyData?: bytes): Promise { + public async finish(earlyData?: bytes): Promise { if (this.isInitiator) { logger('Stage 2 - Initiator sending third handshake message.'); const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeys.publicKey)); diff --git a/src/noise.ts b/src/noise.ts index 5e94cc6..9e06718 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -6,13 +6,15 @@ import ensureBuffer from 'it-buffer'; import pipe from 'it-pipe'; import lp from 'it-length-prefixed'; -import { Handshake } from "./handshake-xx"; +import { Handshake as XX } from "./handshake-xx"; import { generateKeypair } from "./utils"; import { uint16BEDecode, uint16BEEncode } from "./encoder"; import { decryptStream, encryptStream } from "./crypto"; import { bytes } from "./@types/basic"; import { NoiseConnection, PeerId, KeyPair, SecureOutbound } from "./@types/libp2p"; import { Duplex } from "./@types/it-pair"; +import {XXHandshake} from "./handshakes/xx"; +import {HandshakeInterface} from "./@types/handshake-interface"; export type WrappedConnection = ReturnType; @@ -89,24 +91,22 @@ export class Noise implements NoiseConnection { isInitiator: boolean, libp2pPublicKey: bytes, remotePeer: PeerId, - ): Promise { + ): Promise { + // TODO: Implement noise pipes + if (false) { - // TODO: Implement noise pipes } else { - return await this.performXXHandshake(connection, isInitiator, libp2pPublicKey, remotePeer) + const prologue = Buffer.from(this.protocol); + const handshake = new XX(isInitiator, this.privateKey, libp2pPublicKey, prologue, this.staticKeys, connection, remotePeer); + + return await this.performXXHandshake(handshake); } } private async performXXHandshake( - connection: WrappedConnection, - isInitiator: boolean, - libp2pPublicKey: bytes, - remotePeer: PeerId, - ): Promise { - const prologue = Buffer.from(this.protocol); - const handshake = new Handshake(isInitiator, this.privateKey, libp2pPublicKey, prologue, this.staticKeys, connection, remotePeer); - + handshake: XX + ): Promise { try { await handshake.propose(); await handshake.exchange(); @@ -120,7 +120,7 @@ export class Noise implements NoiseConnection { private async createSecureConnection( connection: WrappedConnection, - handshake: Handshake, + handshake: HandshakeInterface, ): Promise { // Create encryption box/unbox wrapper const [secure, user] = DuplexPair(); From a514ededa150cf682a04d9ed5364335fa0f2c8d9 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Fri, 3 Jan 2020 17:28:13 +0100 Subject: [PATCH 04/16] Create IK and read ephemeral keys --- src/handshake-ik.ts | 34 +++++++++++++++++++++++++++++++--- src/handshake-xx.ts | 1 + src/handshakes/xx.ts | 13 +++++++++---- src/noise.ts | 22 ++++++++++++++++------ test/noise.test.ts | 2 +- 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/handshake-ik.ts b/src/handshake-ik.ts index da001bc..e5b8aa9 100644 --- a/src/handshake-ik.ts +++ b/src/handshake-ik.ts @@ -1,10 +1,12 @@ +import {WrappedConnection} from "./noise"; +import {IKHandshake} from "./handshakes/ik"; import {NoiseSession} from "./@types/handshake"; import {bytes, bytes32} from "./@types/basic"; import {KeyPair, PeerId} from "./@types/libp2p"; -import {WrappedConnection} from "./noise"; -import {IKHandshake} from "./handshakes/ik"; +import {HandshakeInterface} from "./@types/handshake-interface"; +import {Buffer} from "buffer"; -export class Handshake { // implements HandshakeHandler +export class Handshake implements HandshakeInterface { public isInitiator: boolean; public session: NoiseSession; @@ -41,4 +43,30 @@ export class Handshake { // implements HandshakeHandler const remoteStaticKeys = this.staticKeys; this.session = this.ik.initSession(this.isInitiator, this.prologue, this.staticKeys, remoteStaticKeys.publicKey); } + + public decrypt(ciphertext: Buffer, session: NoiseSession): Buffer { + const cs = this.getCS(session, false); + return this.ik.decryptWithAd(cs, Buffer.alloc(0), ciphertext); + } + + public encrypt(plaintext: Buffer, session: NoiseSession): Buffer { + const cs = this.getCS(session); + return this.ik.encryptWithAd(cs, Buffer.alloc(0), plaintext); + } + + public getRemoteEphemeralKeys(): KeyPair | undefined { + return this.session.hs.e; + } + + private getCS(session: NoiseSession, encryption = true) { + if (!session.cs1 || !session.cs2) { + throw new Error("Handshake not completed properly, cipher state does not exist."); + } + + if (this.isInitiator) { + return encryption ? session.cs1 : session.cs2; + } else { + return encryption ? session.cs2 : session.cs1; + } + } } diff --git a/src/handshake-xx.ts b/src/handshake-xx.ts index 3500077..19e06a3 100644 --- a/src/handshake-xx.ts +++ b/src/handshake-xx.ts @@ -36,6 +36,7 @@ export class Handshake implements HandshakeInterface { staticKeys: KeyPair, connection: WrappedConnection, remotePeer: PeerId, + ephemeralKeys?: KeyPair, handshake?: XXHandshake, ) { this.isInitiator = isInitiator; diff --git a/src/handshakes/xx.ts b/src/handshakes/xx.ts index 3b8b0a9..7445f6a 100644 --- a/src/handshakes/xx.ts +++ b/src/handshakes/xx.ts @@ -27,9 +27,14 @@ export class XXHandshake extends AbstractHandshake { return { ss, s, rs, psk, re }; } - private writeMessageA(hs: HandshakeState, payload: bytes): MessageBuffer { + private writeMessageA(hs: HandshakeState, payload: bytes, e?: KeyPair): MessageBuffer { const ns = Buffer.alloc(0); - hs.e = generateKeypair(); + + if (e) { + hs.e = e; + } else { + hs.e = generateKeypair(); + } const ne = hs.e.publicKey; @@ -128,10 +133,10 @@ export class XXHandshake extends AbstractHandshake { }; } - public sendMessage(session: NoiseSession, message: bytes): MessageBuffer { + public sendMessage(session: NoiseSession, message: bytes, ephemeral?: KeyPair): MessageBuffer { let messageBuffer: MessageBuffer; if (session.mc.eqn(0)) { - messageBuffer = this.writeMessageA(session.hs, message); + messageBuffer = this.writeMessageA(session.hs, message, ephemeral); } else if (session.mc.eqn(1)) { messageBuffer = this.writeMessageB(session.hs, message); } else if (session.mc.eqn(2)) { diff --git a/src/noise.ts b/src/noise.ts index 9e06718..2451539 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -7,6 +7,7 @@ import pipe from 'it-pipe'; import lp from 'it-length-prefixed'; import { Handshake as XX } from "./handshake-xx"; +import { Handshake as IK } from "./handshake-ik"; import { generateKeypair } from "./utils"; import { uint16BEDecode, uint16BEEncode } from "./encoder"; import { decryptStream, encryptStream } from "./crypto"; @@ -21,6 +22,7 @@ export type WrappedConnection = ReturnType; export class Noise implements NoiseConnection { public protocol = "/noise"; + private readonly prologue = Buffer.from(this.protocol); private readonly privateKey: bytes; private readonly staticKeys: KeyPair; private readonly earlyData?: bytes; @@ -92,21 +94,29 @@ export class Noise implements NoiseConnection { libp2pPublicKey: bytes, remotePeer: PeerId, ): Promise { + // TODO: Implement noise pipes - if (false) { + const IKhandshake = new IK(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer); + if(true) { + // XX fallback + const ephemeralKeys = IKhandshake.getRemoteEphemeralKeys(); + return await this.performXXHandshake(connection, isInitiator, libp2pPublicKey, remotePeer, ephemeralKeys); } else { - const prologue = Buffer.from(this.protocol); - const handshake = new XX(isInitiator, this.privateKey, libp2pPublicKey, prologue, this.staticKeys, connection, remotePeer); - - return await this.performXXHandshake(handshake); + return await this.performXXHandshake(connection, isInitiator, libp2pPublicKey, remotePeer); } } private async performXXHandshake( - handshake: XX + connection: WrappedConnection, + isInitiator: boolean, + libp2pPublicKey: bytes, + remotePeer: PeerId, + ephemeralKeys?: KeyPair, ): Promise { + const handshake = new XX(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer, ephemeralKeys); + try { await handshake.propose(); await handshake.exchange(); diff --git a/test/noise.test.ts b/test/noise.test.ts index 9e8ded3..230c7e8 100644 --- a/test/noise.test.ts +++ b/test/noise.test.ts @@ -61,7 +61,7 @@ describe("Noise", () => { const xx = new XXHandshake(); const { privateKey: libp2pPrivKey, publicKey: libp2pPubKey } = getKeyPairFromPeerId(remotePeer); - const handshake = new Handshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, xx); + const handshake = new Handshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, undefined, xx); let receivedMessageBuffer = decodeMessageBuffer((await wrapped.readLP()).slice()); // The first handshake message contains the initiator's ephemeral public key From 4a9b814e5a6ca154131186078e031e46a4a0c7b2 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Sun, 5 Jan 2020 19:00:16 +0100 Subject: [PATCH 05/16] Create XX fallback flow --- src/handshake-ik.ts | 6 ++- src/handshake-xx-fallback.ts | 89 ++++++++++++++++++++++++++++++++++ src/handshake-xx.ts | 12 ++--- src/handshakes/xx.ts | 24 +++++++++ src/noise.ts | 94 ++++++++++++++++++++++++++---------- test/noise.test.ts | 2 +- 6 files changed, 194 insertions(+), 33 deletions(-) create mode 100644 src/handshake-xx-fallback.ts diff --git a/src/handshake-ik.ts b/src/handshake-ik.ts index e5b8aa9..69123d3 100644 --- a/src/handshake-ik.ts +++ b/src/handshake-ik.ts @@ -54,7 +54,11 @@ export class Handshake implements HandshakeInterface { return this.ik.encryptWithAd(cs, Buffer.alloc(0), plaintext); } - public getRemoteEphemeralKeys(): KeyPair | undefined { + public getRemoteEphemeralKeys(): KeyPair { + if (!this.session.hs.e) { + throw new Error("Ephemeral keys do not exist."); + } + return this.session.hs.e; } diff --git a/src/handshake-xx-fallback.ts b/src/handshake-xx-fallback.ts new file mode 100644 index 0000000..e25647b --- /dev/null +++ b/src/handshake-xx-fallback.ts @@ -0,0 +1,89 @@ +import { Buffer } from "buffer"; + +import { Handshake as XXHandshake } from "./handshake-xx"; +import { XXHandshake as XX } from "./handshakes/xx"; +import { KeyPair, PeerId } from "./@types/libp2p"; +import { bytes, bytes32 } from "./@types/basic"; +import { + createHandshakePayload, + getHandshakePayload, + signEarlyDataPayload, + signPayload, + verifySignedPayload, +} from "./utils"; +import { logger } from "./logger"; +import { decodeMessageBuffer, encodeMessageBuffer } from "./encoder"; +import { WrappedConnection } from "./noise"; + +export class Handshake extends XXHandshake { + private ephemeralKeys: KeyPair; + private initialMsg: bytes; + + constructor( + isInitiator: boolean, + libp2pPrivateKey: bytes, + libp2pPublicKey: bytes, + prologue: bytes32, + staticKeys: KeyPair, + connection: WrappedConnection, + remotePeer: PeerId, + ephemeralKeys: KeyPair, + initialMsg: bytes, + handshake?: XX, + ) { + super(isInitiator, libp2pPrivateKey, libp2pPublicKey, prologue, staticKeys, connection, remotePeer, handshake); + this.ephemeralKeys = ephemeralKeys; + this.initialMsg = initialMsg; + } + + // stage 0 + public async propose(): Promise { + if (this.isInitiator) { + logger("XX Fallback Stage 0 - Initiator starting to send first message."); + const messageBuffer = this.xx.sendMessage(this.session, Buffer.alloc(0), this.ephemeralKeys); + this.connection.writeLP(encodeMessageBuffer(messageBuffer)); + logger("XX Fallback Stage 0 - Initiator finished sending first message."); + } else { + logger("XX Fallback Stage 0 - Responder waiting to receive first message..."); + const receivedMessageBuffer = this.xx.decode0(this.initialMsg); + this.xx.recvMessage(this.session, { + ne: receivedMessageBuffer.ne, + ns: Buffer.alloc(0), + ciphertext: Buffer.alloc(0), + }); + logger("XX Fallback Stage 0 - Responder received first message."); + } + } + + // stage 1 + public async exchange(): Promise { + if (this.isInitiator) { + logger('XX Fallback Stage 1 - Initiator waiting to receive first message from responder...'); + const receivedMessageBuffer = this.xx.decode1(this.initialMsg); + const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer); + logger('XX Fallback Stage 1 - Initiator received the message. Got remote\'s static key.'); + + // logger("Initiator going to check remote's signature..."); + // try { + // await verifySignedPayload(receivedMessageBuffer.ns, plaintext, this.remotePeer.id); + // } catch (e) { + // throw new Error(`Error occurred while verifying signed payload: ${e.message}`); + // } + // logger("All good with the signature!"); + } else { + logger('Stage 1 - Responder sending out first message with signed payload and static key.'); + const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeys.publicKey)); + const signedEarlyDataPayload = signEarlyDataPayload(this.libp2pPrivateKey, Buffer.alloc(0)); + const handshakePayload = await createHandshakePayload( + this.libp2pPublicKey, + this.libp2pPrivateKey, + signedPayload, + signedEarlyDataPayload, + ); + + const messageBuffer = this.xx.sendMessage(this.session, handshakePayload); + this.connection.writeLP(encodeMessageBuffer(messageBuffer)); + logger('Stage 1 - Responder sent the second handshake message with signed payload.') + } + } +} diff --git a/src/handshake-xx.ts b/src/handshake-xx.ts index 19e06a3..7910add 100644 --- a/src/handshake-xx.ts +++ b/src/handshake-xx.ts @@ -20,13 +20,14 @@ export class Handshake implements HandshakeInterface { public isInitiator: boolean; public session: NoiseSession; - private libp2pPrivateKey: bytes; - private libp2pPublicKey: bytes; + protected connection: WrappedConnection; + protected xx: XXHandshake; + + protected libp2pPrivateKey: bytes; + protected libp2pPublicKey: bytes; private prologue: bytes32; - private staticKeys: KeyPair; - private connection: WrappedConnection; + protected staticKeys: KeyPair; private remotePeer: PeerId; - private xx: XXHandshake; constructor( isInitiator: boolean, @@ -36,7 +37,6 @@ export class Handshake implements HandshakeInterface { staticKeys: KeyPair, connection: WrappedConnection, remotePeer: PeerId, - ephemeralKeys?: KeyPair, handshake?: XXHandshake, ) { this.isInitiator = isInitiator; diff --git a/src/handshakes/xx.ts b/src/handshakes/xx.ts index 7445f6a..3fa80a9 100644 --- a/src/handshakes/xx.ts +++ b/src/handshakes/xx.ts @@ -199,4 +199,28 @@ export class XXHandshake extends AbstractHandshake { session.mc = session.mc.add(new BN(1)); return plaintext; } + + public decode0(input: bytes): MessageBuffer { + if (input.length < 32) { + throw new Error("Cannot decode stage 0 MessageBuffer: length less than 32 bytes."); + } + + return { + ne: input.slice(0, 32), + ciphertext: input.slice(32, input.length), + ns: Buffer.alloc(0), + } + } + + public decode1(input: bytes): MessageBuffer { + if (input.length < 96) { + throw new Error("Cannot decode stage 0 MessageBuffer: length less than 96 bytes."); + } + + return { + ne: input.slice(0, 32), + ns: input.slice(32, 80), + ciphertext: input.slice(80, input.length), + } + } } diff --git a/src/noise.ts b/src/noise.ts index 2451539..0033fe6 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -8,6 +8,7 @@ import lp from 'it-length-prefixed'; import { Handshake as XX } from "./handshake-xx"; import { Handshake as IK } from "./handshake-ik"; +import { Handshake as XXFallback } from "./handshake-xx-fallback"; import { generateKeypair } from "./utils"; import { uint16BEDecode, uint16BEEncode } from "./encoder"; import { decryptStream, encryptStream } from "./crypto"; @@ -19,6 +20,13 @@ import {HandshakeInterface} from "./@types/handshake-interface"; export type WrappedConnection = ReturnType; +type HandshakeParams = { + connection: WrappedConnection; + isInitiator: boolean; + libp2pPublicKey: bytes; + remotePeer: PeerId; +}; + export class Noise implements NoiseConnection { public protocol = "/noise"; @@ -52,7 +60,12 @@ export class Noise implements NoiseConnection { public async secureOutbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise { const wrappedConnection = Wrap(connection); const libp2pPublicKey = localPeer.marshalPubKey(); - const handshake = await this.performHandshake(wrappedConnection, true, libp2pPublicKey, remotePeer); + const handshake = await this.performHandshake({ + connection: wrappedConnection, + isInitiator: true, + libp2pPublicKey, + remotePeer, + }); const conn = await this.createSecureConnection(wrappedConnection, handshake); return { @@ -71,7 +84,12 @@ export class Noise implements NoiseConnection { public async secureInbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise { const wrappedConnection = Wrap(connection); const libp2pPublicKey = localPeer.marshalPubKey(); - const handshake = await this.performHandshake(wrappedConnection, false, libp2pPublicKey, remotePeer); + const handshake = await this.performHandshake({ + connection: wrappedConnection, + isInitiator: false, + libp2pPublicKey, + remotePeer + }); const conn = await this.createSecureConnection(wrappedConnection, handshake); return { @@ -88,46 +106,72 @@ export class Noise implements NoiseConnection { * @param libp2pPublicKey * @param remotePeer */ - private async performHandshake( - connection: WrappedConnection, - isInitiator: boolean, - libp2pPublicKey: bytes, - remotePeer: PeerId, - ): Promise { - + private async performHandshake(params: HandshakeParams): Promise { // TODO: Implement noise pipes - const IKhandshake = new IK(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer); - - if(true) { - // XX fallback - const ephemeralKeys = IKhandshake.getRemoteEphemeralKeys(); - return await this.performXXHandshake(connection, isInitiator, libp2pPublicKey, remotePeer, ephemeralKeys); + if (false) { + let IKhandshake; + try { + IKhandshake = await this.performIKHandshake(params); + return IKhandshake; + } catch (e) { + // XX fallback + const ephemeralKeys = IKhandshake.getRemoteEphemeralKeys(); + return await this.performXXFallbackHandshake(params, ephemeralKeys, e.initialMsg); + } } else { - return await this.performXXHandshake(connection, isInitiator, libp2pPublicKey, remotePeer); + return await this.performXXHandshake(params); } } - private async performXXHandshake( - connection: WrappedConnection, - isInitiator: boolean, - libp2pPublicKey: bytes, - remotePeer: PeerId, - ephemeralKeys?: KeyPair, - ): Promise { - const handshake = new XX(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer, ephemeralKeys); + private async performXXFallbackHandshake( + params: HandshakeParams, + ephemeralKeys: KeyPair, + initialMsg: bytes, + ): Promise { + const { isInitiator, libp2pPublicKey, remotePeer, connection } = params; + const handshake = + new XXFallback(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer, ephemeralKeys, initialMsg); try { await handshake.propose(); await handshake.exchange(); await handshake.finish(this.earlyData); } catch (e) { - throw new Error(`Error occurred during handshake: ${e.message}`); + throw new Error(`Error occurred during XX Fallback handshake: ${e.message}`); } return handshake; } + private async performXXHandshake( + params: HandshakeParams, + ): Promise { + const { isInitiator, libp2pPublicKey, remotePeer, connection } = params; + const handshake = new XX(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer); + + try { + await handshake.propose(); + await handshake.exchange(); + await handshake.finish(this.earlyData); + } catch (e) { + throw new Error(`Error occurred during XX handshake: ${e.message}`); + } + + return handshake; + } + + private async performIKHandshake( + params: HandshakeParams, + ): Promise { + const { isInitiator, libp2pPublicKey, remotePeer, connection } = params; + const handshake = new IK(params.isInitiator, this.privateKey, params.libp2pPublicKey, this.prologue, this.staticKeys, params.connection, remotePeer); + + // TODO + + return handshake; + } + private async createSecureConnection( connection: WrappedConnection, handshake: HandshakeInterface, diff --git a/test/noise.test.ts b/test/noise.test.ts index 230c7e8..9e8ded3 100644 --- a/test/noise.test.ts +++ b/test/noise.test.ts @@ -61,7 +61,7 @@ describe("Noise", () => { const xx = new XXHandshake(); const { privateKey: libp2pPrivKey, publicKey: libp2pPubKey } = getKeyPairFromPeerId(remotePeer); - const handshake = new Handshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, undefined, xx); + const handshake = new Handshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, xx); let receivedMessageBuffer = decodeMessageBuffer((await wrapped.readLP()).slice()); // The first handshake message contains the initiator's ephemeral public key From 096a30b289a95e77aeee681bc8c0d547c47295d8 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Sun, 5 Jan 2020 19:09:59 +0100 Subject: [PATCH 06/16] Better naming --- src/handshake-ik.ts | 8 ++++---- src/handshake-xx-fallback.ts | 2 +- src/handshake-xx.ts | 8 ++++---- src/handshakes/ik.ts | 2 +- src/handshakes/xx.ts | 4 ++-- src/noise.ts | 1 - test/handshakes/ik.test.ts | 6 +++--- test/handshakes/xx.test.ts | 12 ++++++------ test/noise.test.ts | 4 ++-- 9 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/handshake-ik.ts b/src/handshake-ik.ts index 69123d3..5f173f2 100644 --- a/src/handshake-ik.ts +++ b/src/handshake-ik.ts @@ -1,5 +1,5 @@ import {WrappedConnection} from "./noise"; -import {IKHandshake} from "./handshakes/ik"; +import {IK} from "./handshakes/ik"; import {NoiseSession} from "./@types/handshake"; import {bytes, bytes32} from "./@types/basic"; import {KeyPair, PeerId} from "./@types/libp2p"; @@ -16,7 +16,7 @@ export class Handshake implements HandshakeInterface { private staticKeys: KeyPair; private connection: WrappedConnection; private remotePeer: PeerId; - private ik: IKHandshake; + private ik: IK; constructor( isInitiator: boolean, @@ -26,7 +26,7 @@ export class Handshake implements HandshakeInterface { staticKeys: KeyPair, connection: WrappedConnection, remotePeer: PeerId, - handshake?: IKHandshake, + handshake?: IK, ) { this.isInitiator = isInitiator; this.libp2pPrivateKey = libp2pPrivateKey; @@ -36,7 +36,7 @@ export class Handshake implements HandshakeInterface { this.connection = connection; this.remotePeer = remotePeer; - this.ik = handshake || new IKHandshake(); + this.ik = handshake || new IK(); // Dummy data // TODO: Load remote static keys if found diff --git a/src/handshake-xx-fallback.ts b/src/handshake-xx-fallback.ts index e25647b..1666785 100644 --- a/src/handshake-xx-fallback.ts +++ b/src/handshake-xx-fallback.ts @@ -1,7 +1,7 @@ import { Buffer } from "buffer"; import { Handshake as XXHandshake } from "./handshake-xx"; -import { XXHandshake as XX } from "./handshakes/xx"; +import { XX } from "./handshakes/xx"; import { KeyPair, PeerId } from "./@types/libp2p"; import { bytes, bytes32 } from "./@types/basic"; import { diff --git a/src/handshake-xx.ts b/src/handshake-xx.ts index 7910add..4e34c54 100644 --- a/src/handshake-xx.ts +++ b/src/handshake-xx.ts @@ -1,6 +1,6 @@ import { Buffer } from "buffer"; -import { XXHandshake } from "./handshakes/xx"; +import { XX } from "./handshakes/xx"; import { KeyPair, PeerId } from "./@types/libp2p"; import { bytes, bytes32 } from "./@types/basic"; import { NoiseSession } from "./@types/handshake"; @@ -21,7 +21,7 @@ export class Handshake implements HandshakeInterface { public session: NoiseSession; protected connection: WrappedConnection; - protected xx: XXHandshake; + protected xx: XX; protected libp2pPrivateKey: bytes; protected libp2pPublicKey: bytes; @@ -37,7 +37,7 @@ export class Handshake implements HandshakeInterface { staticKeys: KeyPair, connection: WrappedConnection, remotePeer: PeerId, - handshake?: XXHandshake, + handshake?: XX, ) { this.isInitiator = isInitiator; this.libp2pPrivateKey = libp2pPrivateKey; @@ -47,7 +47,7 @@ export class Handshake implements HandshakeInterface { this.connection = connection; this.remotePeer = remotePeer; - this.xx = handshake || new XXHandshake(); + this.xx = handshake || new XX(); this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeys); } diff --git a/src/handshakes/ik.ts b/src/handshakes/ik.ts index f7c04ff..8d41e49 100644 --- a/src/handshakes/ik.ts +++ b/src/handshakes/ik.ts @@ -8,7 +8,7 @@ import {AbstractHandshake} from "./abstract-handshake"; import {KeyPair} from "../@types/libp2p"; -export class IKHandshake extends AbstractHandshake { +export class IK extends AbstractHandshake { public initSession(initiator: boolean, prologue: bytes32, s: KeyPair, rs: bytes32): NoiseSession { const psk = this.createEmptyKey(); diff --git a/src/handshakes/xx.ts b/src/handshakes/xx.ts index 3fa80a9..857a9b5 100644 --- a/src/handshakes/xx.ts +++ b/src/handshakes/xx.ts @@ -3,12 +3,12 @@ import { BN } from 'bn.js'; import { bytes32, bytes } from '../@types/basic' import { KeyPair } from '../@types/libp2p' -import {generateKeypair, getHkdf, isValidPublicKey} from '../utils'; +import {generateKeypair, isValidPublicKey} from '../utils'; import { HandshakeState, MessageBuffer, NoiseSession } from "../@types/handshake"; import {AbstractHandshake} from "./abstract-handshake"; -export class XXHandshake extends AbstractHandshake { +export class XX extends AbstractHandshake { private initializeInitiator(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): HandshakeState { const name = "Noise_XX_25519_ChaChaPoly_SHA256"; const ss = this.initializeSymmetric(name); diff --git a/src/noise.ts b/src/noise.ts index 0033fe6..e0b0b06 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -15,7 +15,6 @@ import { decryptStream, encryptStream } from "./crypto"; import { bytes } from "./@types/basic"; import { NoiseConnection, PeerId, KeyPair, SecureOutbound } from "./@types/libp2p"; import { Duplex } from "./@types/it-pair"; -import {XXHandshake} from "./handshakes/xx"; import {HandshakeInterface} from "./@types/handshake-interface"; export type WrappedConnection = ReturnType; diff --git a/test/handshakes/ik.test.ts b/test/handshakes/ik.test.ts index 6a4bf8e..4597e0f 100644 --- a/test/handshakes/ik.test.ts +++ b/test/handshakes/ik.test.ts @@ -1,5 +1,5 @@ import {Buffer} from "buffer"; -import {IKHandshake} from "../../src/handshakes/ik"; +import {IK} from "../../src/handshakes/ik"; import {KeyPair} from "../../src/@types/libp2p"; import {createHandshakePayload, generateKeypair, getHandshakePayload} from "../../src/utils"; import {assert, expect} from "chai"; @@ -10,8 +10,8 @@ describe("Index", () => { it("Test complete IK handshake", async () => { try { - const ikI = new IKHandshake(); - const ikR = new IKHandshake(); + const ikI = new IK(); + const ikR = new IK(); // Generate static noise keys const kpInitiator: KeyPair = await generateKeypair(); diff --git a/test/handshakes/xx.test.ts b/test/handshakes/xx.test.ts index b6d93f4..b0713b3 100644 --- a/test/handshakes/xx.test.ts +++ b/test/handshakes/xx.test.ts @@ -1,7 +1,7 @@ import { expect, assert } from "chai"; import { Buffer } from 'buffer'; -import { XXHandshake } from "../../src/handshakes/xx"; +import { XX } from "../../src/handshakes/xx"; import { KeyPair } from "../../src/@types/libp2p"; import { generateEd25519Keys } from "../utils"; import {createHandshakePayload, generateKeypair, getHandshakePayload, getHkdf} from "../../src/utils"; @@ -11,7 +11,7 @@ describe("Index", () => { it("Test creating new XX session", async () => { try { - const xx = new XXHandshake(); + const xx = new XX(); const kpInitiator: KeyPair = await generateKeypair(); const kpResponder: KeyPair = await generateKeypair(); @@ -23,7 +23,7 @@ describe("Index", () => { }); it("Test get HKDF", async () => { - const xx = new XXHandshake(); + const xx = new XX(); const ckBytes = Buffer.from('4e6f6973655f58585f32353531395f58436861436861506f6c795f53484132353600000000000000000000000000000000000000000000000000000000000000', 'hex'); const ikm = Buffer.from('a3eae50ea37a47e8a7aa0c7cd8e16528670536dcd538cebfd724fb68ce44f1910ad898860666227d4e8dd50d22a9a64d1c0a6f47ace092510161e9e442953da3', 'hex'); const ck = Buffer.alloc(32); @@ -104,7 +104,7 @@ describe("Index", () => { it("Test handshake", async () => { try { - const xx = new XXHandshake(); + const xx = new XX(); await doHandshake(xx); } catch (e) { assert(false, e.message); @@ -113,7 +113,7 @@ describe("Index", () => { it("Test symmetric encrypt and decrypt", async () => { try { - const xx = new XXHandshake(); + const xx = new XX(); const { nsInit, nsResp } = await doHandshake(xx); const ad = Buffer.from("authenticated"); const message = Buffer.from("HelloCrypto"); @@ -130,7 +130,7 @@ describe("Index", () => { it("Test multiple messages encryption and decryption", async () => { try { - const xx = new XXHandshake(); + const xx = new XX(); const { nsInit, nsResp } = await doHandshake(xx); const ad = Buffer.from("authenticated"); const message = Buffer.from("ethereum1"); diff --git a/test/noise.test.ts b/test/noise.test.ts index 9e8ded3..8f78b76 100644 --- a/test/noise.test.ts +++ b/test/noise.test.ts @@ -13,7 +13,7 @@ import { signPayload } from "../src/utils"; import { decodeMessageBuffer, encodeMessageBuffer } from "../src/encoder"; -import {XXHandshake} from "../src/handshakes/xx"; +import {XX} from "../src/handshakes/xx"; import {Buffer} from "buffer"; import {getKeyPairFromPeerId} from "./utils"; @@ -58,7 +58,7 @@ describe("Noise", () => { const wrapped = Wrap(inboundConnection); const prologue = Buffer.from('/noise'); const staticKeys = generateKeypair(); - const xx = new XXHandshake(); + const xx = new XX(); const { privateKey: libp2pPrivKey, publicKey: libp2pPubKey } = getKeyPairFromPeerId(remotePeer); const handshake = new Handshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, xx); From fc818c746cbadcf41347b0cb1cdadf949703b650 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Tue, 7 Jan 2020 10:16:57 +0100 Subject: [PATCH 07/16] Create fallback test --- src/handshake-xx-fallback.ts | 15 ++-- src/handshake-xx.ts | 6 +- test/xx-fallback-handshake.test.ts | 83 +++++++++++++++++++ ...handshake.test.ts => xx-handshake.test.ts} | 2 +- 4 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 test/xx-fallback-handshake.test.ts rename test/{handshake.test.ts => xx-handshake.test.ts} (99%) diff --git a/src/handshake-xx-fallback.ts b/src/handshake-xx-fallback.ts index 1666785..594f6e4 100644 --- a/src/handshake-xx-fallback.ts +++ b/src/handshake-xx-fallback.ts @@ -39,17 +39,16 @@ export class Handshake extends XXHandshake { // stage 0 public async propose(): Promise { if (this.isInitiator) { - logger("XX Fallback Stage 0 - Initiator starting to send first message."); - const messageBuffer = this.xx.sendMessage(this.session, Buffer.alloc(0), this.ephemeralKeys); - this.connection.writeLP(encodeMessageBuffer(messageBuffer)); - logger("XX Fallback Stage 0 - Initiator finished sending first message."); + this.xx.sendMessage(this.session, Buffer.alloc(0), this.ephemeralKeys); + logger("XX Fallback Stage 0 - Initialized state as the first message was sent by initiator."); } else { logger("XX Fallback Stage 0 - Responder waiting to receive first message..."); - const receivedMessageBuffer = this.xx.decode0(this.initialMsg); + const receivedMessageBuffer = decodeMessageBuffer(this.initialMsg); + console.log("receivedMessageBuffer: ", receivedMessageBuffer) this.xx.recvMessage(this.session, { ne: receivedMessageBuffer.ne, - ns: Buffer.alloc(0), - ciphertext: Buffer.alloc(0), + ns: Buffer.alloc(32), + ciphertext: Buffer.alloc(32), }); logger("XX Fallback Stage 0 - Responder received first message."); } @@ -59,7 +58,7 @@ export class Handshake extends XXHandshake { public async exchange(): Promise { if (this.isInitiator) { logger('XX Fallback Stage 1 - Initiator waiting to receive first message from responder...'); - const receivedMessageBuffer = this.xx.decode1(this.initialMsg); + const receivedMessageBuffer = decodeMessageBuffer(this.initialMsg); const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer); logger('XX Fallback Stage 1 - Initiator received the message. Got remote\'s static key.'); diff --git a/src/handshake-xx.ts b/src/handshake-xx.ts index 4e34c54..61611cc 100644 --- a/src/handshake-xx.ts +++ b/src/handshake-xx.ts @@ -22,12 +22,12 @@ export class Handshake implements HandshakeInterface { protected connection: WrappedConnection; protected xx: XX; - protected libp2pPrivateKey: bytes; protected libp2pPublicKey: bytes; - private prologue: bytes32; protected staticKeys: KeyPair; - private remotePeer: PeerId; + protected remotePeer: PeerId; + + private prologue: bytes32; constructor( isInitiator: boolean, diff --git a/test/xx-fallback-handshake.test.ts b/test/xx-fallback-handshake.test.ts new file mode 100644 index 0000000..9eb7b70 --- /dev/null +++ b/test/xx-fallback-handshake.test.ts @@ -0,0 +1,83 @@ +import Wrap from "it-pb-rpc"; +import {Buffer} from "buffer"; +import Duplex from 'it-pair/duplex'; + +import { + createHandshakePayload, + generateKeypair, + getHandshakePayload, + signEarlyDataPayload, + signPayload +} from "../src/utils"; +import {generateEd25519Keys, getKeyPairFromPeerId} from "./utils"; +import {Handshake} from "../src/handshake-xx-fallback"; +import {createPeerIdsFromFixtures} from "./fixtures/peer"; +import {assert} from "chai"; +import {encodeMessageBuffer} from "../src/encoder"; + +describe("XX Fallback Handshake", () => { + let peerA, peerB, fakePeer; + + before(async () => { + [peerA, peerB] = await createPeerIdsFromFixtures(2); + }); + + it("should make handshake with received ephemeral key (from initial IK message)", async () => { + try { + const duplex = Duplex(); + const connectionFrom = Wrap(duplex[0]); + const connectionTo = Wrap(duplex[1]); + + const prologue = Buffer.from('/noise'); + const staticKeysInitiator = generateKeypair(); + const staticKeysResponder = generateKeypair(); + + const {privateKey: initiatorPrivKey, publicKey: initiatorPubKey} = getKeyPairFromPeerId(peerA); + const {privateKey: responderPrivKey, publicKey: responderPubKey} = getKeyPairFromPeerId(peerB); + + const signedPayload = signPayload(initiatorPrivKey, getHandshakePayload(staticKeysInitiator.publicKey)); + const signedEarlyDataPayload = signEarlyDataPayload(initiatorPrivKey, Buffer.alloc(0)); + const handshakePayload = await createHandshakePayload( + initiatorPubKey, + initiatorPrivKey, + signedPayload, + signedEarlyDataPayload, + ); + const initialMsg = encodeMessageBuffer({ + ne: staticKeysInitiator.publicKey, + ns: Buffer.alloc(32), + ciphertext: handshakePayload, + }); + + const handshakeInit = + new Handshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB, staticKeysInitiator, initialMsg); + + const handshakeResp = + new Handshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA, staticKeysInitiator, initialMsg); + + + await handshakeInit.propose(); + await handshakeResp.propose(); + + await handshakeResp.exchange(); + await handshakeInit.exchange(); + + await handshakeInit.finish(); + await handshakeResp.finish(); + + const sessionInitator = handshakeInit.session; + const sessionResponder = handshakeResp.session; + + // Test shared key + if (sessionInitator.cs1 && sessionResponder.cs1 && sessionInitator.cs2 && sessionResponder.cs2) { + assert(sessionInitator.cs1.k.equals(sessionResponder.cs1.k)); + assert(sessionInitator.cs2.k.equals(sessionResponder.cs2.k)); + } else { + assert(false); + } + } catch (e) { + console.error(e); + assert(false, e.message); + } + }); +}) diff --git a/test/handshake.test.ts b/test/xx-handshake.test.ts similarity index 99% rename from test/handshake.test.ts rename to test/xx-handshake.test.ts index 1ed6fa7..e1f2cb2 100644 --- a/test/handshake.test.ts +++ b/test/xx-handshake.test.ts @@ -9,7 +9,7 @@ import {createPeerIdsFromFixtures} from "./fixtures/peer"; import {getKeyPairFromPeerId} from "./utils"; -describe("Handshake", () => { +describe("XX Handshake", () => { let peerA, peerB, fakePeer; before(async () => { From dff2450b181c5911037e7a72c991253bbd89fc4c Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Tue, 7 Jan 2020 10:29:40 +0100 Subject: [PATCH 08/16] Update encoder functions --- src/encoder.ts | 30 +++++++++++++++++++++++++----- src/handshake-xx-fallback.ts | 8 ++++---- src/handshake-xx.ts | 14 +++++++------- src/handshakes/xx.ts | 24 ------------------------ test/noise.test.ts | 8 ++++---- test/xx-fallback-handshake.test.ts | 4 ++-- 6 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/encoder.ts b/src/encoder.ts index e4e2c30..4f869e6 100644 --- a/src/encoder.ts +++ b/src/encoder.ts @@ -14,14 +14,34 @@ export const uint16BEDecode = data => { }; uint16BEDecode.bytes = 2; -export function encodeMessageBuffer(message: MessageBuffer): bytes { +export function encode0(message: MessageBuffer): bytes { + return Buffer.concat([message.ne, message.ciphertext]); +} + +export function encode1(message: MessageBuffer): bytes { return Buffer.concat([message.ne, message.ns, message.ciphertext]); } -export function decodeMessageBuffer(message: bytes): MessageBuffer { +export function decode0(input: bytes): MessageBuffer { + if (input.length < 32) { + throw new Error("Cannot decode stage 0 MessageBuffer: length less than 32 bytes."); + } + return { - ne: message.slice(0, 32), - ns: message.slice(32, 64), - ciphertext: message.slice(64, message.length), + ne: input.slice(0, 32), + ciphertext: input.slice(32, input.length), + ns: Buffer.alloc(0), + } +} + +export function decode1(input: bytes): MessageBuffer { + if (input.length < 96) { + throw new Error("Cannot decode stage 0 MessageBuffer: length less than 96 bytes."); + } + + return { + ne: input.slice(0, 32), + ns: input.slice(32, 64), + ciphertext: input.slice(64, input.length), } } diff --git a/src/handshake-xx-fallback.ts b/src/handshake-xx-fallback.ts index 594f6e4..d25a7cf 100644 --- a/src/handshake-xx-fallback.ts +++ b/src/handshake-xx-fallback.ts @@ -12,8 +12,8 @@ import { verifySignedPayload, } from "./utils"; import { logger } from "./logger"; -import { decodeMessageBuffer, encodeMessageBuffer } from "./encoder"; import { WrappedConnection } from "./noise"; +import {decode0, decode1, encode1} from "./encoder"; export class Handshake extends XXHandshake { private ephemeralKeys: KeyPair; @@ -43,7 +43,7 @@ export class Handshake extends XXHandshake { logger("XX Fallback Stage 0 - Initialized state as the first message was sent by initiator."); } else { logger("XX Fallback Stage 0 - Responder waiting to receive first message..."); - const receivedMessageBuffer = decodeMessageBuffer(this.initialMsg); + const receivedMessageBuffer = decode0(this.initialMsg); console.log("receivedMessageBuffer: ", receivedMessageBuffer) this.xx.recvMessage(this.session, { ne: receivedMessageBuffer.ne, @@ -58,7 +58,7 @@ export class Handshake extends XXHandshake { public async exchange(): Promise { if (this.isInitiator) { logger('XX Fallback Stage 1 - Initiator waiting to receive first message from responder...'); - const receivedMessageBuffer = decodeMessageBuffer(this.initialMsg); + const receivedMessageBuffer = decode1(this.initialMsg); const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer); logger('XX Fallback Stage 1 - Initiator received the message. Got remote\'s static key.'); @@ -81,7 +81,7 @@ export class Handshake extends XXHandshake { ); const messageBuffer = this.xx.sendMessage(this.session, handshakePayload); - this.connection.writeLP(encodeMessageBuffer(messageBuffer)); + this.connection.writeLP(encode1(messageBuffer)); logger('Stage 1 - Responder sent the second handshake message with signed payload.') } } diff --git a/src/handshake-xx.ts b/src/handshake-xx.ts index 61611cc..f5fd353 100644 --- a/src/handshake-xx.ts +++ b/src/handshake-xx.ts @@ -13,7 +13,7 @@ import { verifySignedPayload, } from "./utils"; import { logger } from "./logger"; -import { decodeMessageBuffer, encodeMessageBuffer } from "./encoder"; +import { decode0, decode1, encode0, encode1 } from "./encoder"; import { WrappedConnection } from "./noise"; export class Handshake implements HandshakeInterface { @@ -56,11 +56,11 @@ export class Handshake implements HandshakeInterface { if (this.isInitiator) { logger("Stage 0 - Initiator starting to send first message."); const messageBuffer = this.xx.sendMessage(this.session, Buffer.alloc(0)); - this.connection.writeLP(encodeMessageBuffer(messageBuffer)); + this.connection.writeLP(encode0(messageBuffer)); logger("Stage 0 - Initiator finished sending first message."); } else { logger("Stage 0 - Responder waiting to receive first message..."); - const receivedMessageBuffer = decodeMessageBuffer((await this.connection.readLP()).slice()); + const receivedMessageBuffer = decode0((await this.connection.readLP()).slice()); this.xx.recvMessage(this.session, receivedMessageBuffer); logger("Stage 0 - Responder received first message."); } @@ -70,7 +70,7 @@ export class Handshake implements HandshakeInterface { public async exchange(): Promise { if (this.isInitiator) { logger('Stage 1 - Initiator waiting to receive first message from responder...'); - const receivedMessageBuffer = decodeMessageBuffer((await this.connection.readLP()).slice()); + const receivedMessageBuffer = decode1((await this.connection.readLP()).slice()); const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer); logger('Stage 1 - Initiator received the message. Got remote\'s static key.'); @@ -93,7 +93,7 @@ export class Handshake implements HandshakeInterface { ); const messageBuffer = this.xx.sendMessage(this.session, handshakePayload); - this.connection.writeLP(encodeMessageBuffer(messageBuffer)); + this.connection.writeLP(encode1(messageBuffer)); logger('Stage 1 - Responder sent the second handshake message with signed payload.') } } @@ -111,11 +111,11 @@ export class Handshake implements HandshakeInterface { signedEarlyDataPayload ); const messageBuffer = this.xx.sendMessage(this.session, handshakePayload); - this.connection.writeLP(encodeMessageBuffer(messageBuffer)); + this.connection.writeLP(encode1(messageBuffer)); logger('Stage 2 - Initiator sent message with signed payload.'); } else { logger('Stage 2 - Responder waiting for third handshake message...'); - const receivedMessageBuffer = decodeMessageBuffer((await this.connection.readLP()).slice()); + const receivedMessageBuffer = decode1((await this.connection.readLP()).slice()); const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer); logger('Stage 2 - Responder received the message, finished handshake. Got remote\'s static key.'); diff --git a/src/handshakes/xx.ts b/src/handshakes/xx.ts index 857a9b5..110dc7e 100644 --- a/src/handshakes/xx.ts +++ b/src/handshakes/xx.ts @@ -199,28 +199,4 @@ export class XX extends AbstractHandshake { session.mc = session.mc.add(new BN(1)); return plaintext; } - - public decode0(input: bytes): MessageBuffer { - if (input.length < 32) { - throw new Error("Cannot decode stage 0 MessageBuffer: length less than 32 bytes."); - } - - return { - ne: input.slice(0, 32), - ciphertext: input.slice(32, input.length), - ns: Buffer.alloc(0), - } - } - - public decode1(input: bytes): MessageBuffer { - if (input.length < 96) { - throw new Error("Cannot decode stage 0 MessageBuffer: length less than 96 bytes."); - } - - return { - ne: input.slice(0, 32), - ns: input.slice(32, 80), - ciphertext: input.slice(80, input.length), - } - } } diff --git a/test/noise.test.ts b/test/noise.test.ts index 8f78b76..00297a0 100644 --- a/test/noise.test.ts +++ b/test/noise.test.ts @@ -12,7 +12,7 @@ import { getHandshakePayload, signPayload } from "../src/utils"; -import { decodeMessageBuffer, encodeMessageBuffer } from "../src/encoder"; +import {decode0, decode1, encode1} from "../src/encoder"; import {XX} from "../src/handshakes/xx"; import {Buffer} from "buffer"; import {getKeyPairFromPeerId} from "./utils"; @@ -63,7 +63,7 @@ describe("Noise", () => { const handshake = new Handshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, xx); - let receivedMessageBuffer = decodeMessageBuffer((await wrapped.readLP()).slice()); + let receivedMessageBuffer = decode0((await wrapped.readLP()).slice()); // The first handshake message contains the initiator's ephemeral public key expect(receivedMessageBuffer.ne.length).equal(32); xx.recvMessage(handshake.session, receivedMessageBuffer); @@ -73,10 +73,10 @@ describe("Noise", () => { const handshakePayload = await createHandshakePayload(libp2pPubKey, libp2pPrivKey, signedPayload); const messageBuffer = xx.sendMessage(handshake.session, handshakePayload); - wrapped.writeLP(encodeMessageBuffer(messageBuffer)); + wrapped.writeLP(encode1(messageBuffer)); // Stage 2 - finish handshake - receivedMessageBuffer = decodeMessageBuffer((await wrapped.readLP()).slice()); + receivedMessageBuffer = decode1((await wrapped.readLP()).slice()); xx.recvMessage(handshake.session, receivedMessageBuffer); return {wrapped, handshake}; })(), diff --git a/test/xx-fallback-handshake.test.ts b/test/xx-fallback-handshake.test.ts index 9eb7b70..8c742ce 100644 --- a/test/xx-fallback-handshake.test.ts +++ b/test/xx-fallback-handshake.test.ts @@ -13,7 +13,7 @@ import {generateEd25519Keys, getKeyPairFromPeerId} from "./utils"; import {Handshake} from "../src/handshake-xx-fallback"; import {createPeerIdsFromFixtures} from "./fixtures/peer"; import {assert} from "chai"; -import {encodeMessageBuffer} from "../src/encoder"; +import {encode0} from "../src/encoder"; describe("XX Fallback Handshake", () => { let peerA, peerB, fakePeer; @@ -43,7 +43,7 @@ describe("XX Fallback Handshake", () => { signedPayload, signedEarlyDataPayload, ); - const initialMsg = encodeMessageBuffer({ + const initialMsg = encode0({ ne: staticKeysInitiator.publicKey, ns: Buffer.alloc(32), ciphertext: handshakePayload, From 3bd2145079734c0fc275b53aa11a20c2776be351 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Tue, 7 Jan 2020 13:05:48 +0100 Subject: [PATCH 09/16] Fix test --- src/handshake-xx-fallback.ts | 23 ++++++++++++----------- test/xx-fallback-handshake.test.ts | 13 ++++++------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/handshake-xx-fallback.ts b/src/handshake-xx-fallback.ts index d25a7cf..17810e3 100644 --- a/src/handshake-xx-fallback.ts +++ b/src/handshake-xx-fallback.ts @@ -44,11 +44,10 @@ export class Handshake extends XXHandshake { } else { logger("XX Fallback Stage 0 - Responder waiting to receive first message..."); const receivedMessageBuffer = decode0(this.initialMsg); - console.log("receivedMessageBuffer: ", receivedMessageBuffer) this.xx.recvMessage(this.session, { ne: receivedMessageBuffer.ne, - ns: Buffer.alloc(32), - ciphertext: Buffer.alloc(32), + ns: Buffer.alloc(0), + ciphertext: Buffer.alloc(0), }); logger("XX Fallback Stage 0 - Responder received first message."); } @@ -58,17 +57,19 @@ export class Handshake extends XXHandshake { public async exchange(): Promise { if (this.isInitiator) { logger('XX Fallback Stage 1 - Initiator waiting to receive first message from responder...'); - const receivedMessageBuffer = decode1(this.initialMsg); + const receivedMessageBuffer = decode1((await this.connection.readLP()).slice()); + // const receivedMessageBuffer = decode1(this.initialMsg); + logger("Initiator receivedMessageBuffer in stage 1", receivedMessageBuffer); const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer); logger('XX Fallback Stage 1 - Initiator received the message. Got remote\'s static key.'); - // logger("Initiator going to check remote's signature..."); - // try { - // await verifySignedPayload(receivedMessageBuffer.ns, plaintext, this.remotePeer.id); - // } catch (e) { - // throw new Error(`Error occurred while verifying signed payload: ${e.message}`); - // } - // logger("All good with the signature!"); + logger("Initiator going to check remote's signature..."); + try { + await verifySignedPayload(receivedMessageBuffer.ns, plaintext, this.remotePeer.id); + } catch (e) { + throw new Error(`Error occurred while verifying signed payload: ${e.message}`); + } + logger("All good with the signature!"); } else { logger('Stage 1 - Responder sending out first message with signed payload and static key.'); const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeys.publicKey)); diff --git a/test/xx-fallback-handshake.test.ts b/test/xx-fallback-handshake.test.ts index 8c742ce..7902e4a 100644 --- a/test/xx-fallback-handshake.test.ts +++ b/test/xx-fallback-handshake.test.ts @@ -13,7 +13,7 @@ import {generateEd25519Keys, getKeyPairFromPeerId} from "./utils"; import {Handshake} from "../src/handshake-xx-fallback"; import {createPeerIdsFromFixtures} from "./fixtures/peer"; import {assert} from "chai"; -import {encode0} from "../src/encoder"; +import {encode0, encode1} from "../src/encoder"; describe("XX Fallback Handshake", () => { let peerA, peerB, fakePeer; @@ -31,29 +31,28 @@ describe("XX Fallback Handshake", () => { const prologue = Buffer.from('/noise'); const staticKeysInitiator = generateKeypair(); const staticKeysResponder = generateKeypair(); + const ephemeralKeys = generateKeypair(); const {privateKey: initiatorPrivKey, publicKey: initiatorPubKey} = getKeyPairFromPeerId(peerA); const {privateKey: responderPrivKey, publicKey: responderPubKey} = getKeyPairFromPeerId(peerB); const signedPayload = signPayload(initiatorPrivKey, getHandshakePayload(staticKeysInitiator.publicKey)); - const signedEarlyDataPayload = signEarlyDataPayload(initiatorPrivKey, Buffer.alloc(0)); const handshakePayload = await createHandshakePayload( initiatorPubKey, initiatorPrivKey, signedPayload, - signedEarlyDataPayload, ); const initialMsg = encode0({ - ne: staticKeysInitiator.publicKey, - ns: Buffer.alloc(32), + ne: ephemeralKeys.publicKey, + ns: Buffer.alloc(0), ciphertext: handshakePayload, }); const handshakeInit = - new Handshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB, staticKeysInitiator, initialMsg); + new Handshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB, ephemeralKeys, initialMsg); const handshakeResp = - new Handshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA, staticKeysInitiator, initialMsg); + new Handshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA, ephemeralKeys, initialMsg); await handshakeInit.propose(); From 73c336088e4d1052e683974ab2374fcee8ac310b Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Tue, 7 Jan 2020 13:09:35 +0100 Subject: [PATCH 10/16] Lint fix --- src/handshake-xx-fallback.ts | 4 ++-- src/noise.ts | 2 +- src/utils.ts | 2 +- test/xx-fallback-handshake.test.ts | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/handshake-xx-fallback.ts b/src/handshake-xx-fallback.ts index 17810e3..900705e 100644 --- a/src/handshake-xx-fallback.ts +++ b/src/handshake-xx-fallback.ts @@ -71,7 +71,7 @@ export class Handshake extends XXHandshake { } logger("All good with the signature!"); } else { - logger('Stage 1 - Responder sending out first message with signed payload and static key.'); + logger('XX Fallback Stage 1 - Responder sending out first message with signed payload and static key.'); const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeys.publicKey)); const signedEarlyDataPayload = signEarlyDataPayload(this.libp2pPrivateKey, Buffer.alloc(0)); const handshakePayload = await createHandshakePayload( @@ -83,7 +83,7 @@ export class Handshake extends XXHandshake { const messageBuffer = this.xx.sendMessage(this.session, handshakePayload); this.connection.writeLP(encode1(messageBuffer)); - logger('Stage 1 - Responder sent the second handshake message with signed payload.') + logger('XX Fallback Stage 1 - Responder sent the second handshake message with signed payload.') } } } diff --git a/src/noise.ts b/src/noise.ts index e0b0b06..81656cf 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -164,7 +164,7 @@ export class Noise implements NoiseConnection { params: HandshakeParams, ): Promise { const { isInitiator, libp2pPublicKey, remotePeer, connection } = params; - const handshake = new IK(params.isInitiator, this.privateKey, params.libp2pPublicKey, this.prologue, this.staticKeys, params.connection, remotePeer); + const handshake = new IK(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer); // TODO diff --git a/src/utils.ts b/src/utils.ts index 005f826..ccd6271 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -46,7 +46,7 @@ export async function createHandshakePayload( } -export function signPayload(libp2pPrivateKey: bytes, payload: bytes) { +export function signPayload(libp2pPrivateKey: bytes, payload: bytes): bytes { return ed25519.sign(payload, libp2pPrivateKey); } diff --git a/test/xx-fallback-handshake.test.ts b/test/xx-fallback-handshake.test.ts index 7902e4a..130a8ff 100644 --- a/test/xx-fallback-handshake.test.ts +++ b/test/xx-fallback-handshake.test.ts @@ -6,7 +6,6 @@ import { createHandshakePayload, generateKeypair, getHandshakePayload, - signEarlyDataPayload, signPayload } from "../src/utils"; import {generateEd25519Keys, getKeyPairFromPeerId} from "./utils"; From 28bf51c4929ac62d91211c37a9b8502e0ff3b8a7 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Tue, 7 Jan 2020 13:20:42 +0100 Subject: [PATCH 11/16] Make ephemeral keypair optional --- src/handshake-xx-fallback.ts | 8 +++++--- src/noise.ts | 2 +- test/xx-fallback-handshake.test.ts | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/handshake-xx-fallback.ts b/src/handshake-xx-fallback.ts index 900705e..a7c0788 100644 --- a/src/handshake-xx-fallback.ts +++ b/src/handshake-xx-fallback.ts @@ -16,7 +16,7 @@ import { WrappedConnection } from "./noise"; import {decode0, decode1, encode1} from "./encoder"; export class Handshake extends XXHandshake { - private ephemeralKeys: KeyPair; + private ephemeralKeys?: KeyPair; private initialMsg: bytes; constructor( @@ -27,12 +27,14 @@ export class Handshake extends XXHandshake { staticKeys: KeyPair, connection: WrappedConnection, remotePeer: PeerId, - ephemeralKeys: KeyPair, initialMsg: bytes, + ephemeralKeys?: KeyPair, handshake?: XX, ) { super(isInitiator, libp2pPrivateKey, libp2pPublicKey, prologue, staticKeys, connection, remotePeer, handshake); - this.ephemeralKeys = ephemeralKeys; + if (ephemeralKeys) { + this.ephemeralKeys = ephemeralKeys; + } this.initialMsg = initialMsg; } diff --git a/src/noise.ts b/src/noise.ts index 81656cf..249e76d 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -130,7 +130,7 @@ export class Noise implements NoiseConnection { ): Promise { const { isInitiator, libp2pPublicKey, remotePeer, connection } = params; const handshake = - new XXFallback(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer, ephemeralKeys, initialMsg); + new XXFallback(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer, initialMsg, ephemeralKeys); try { await handshake.propose(); diff --git a/test/xx-fallback-handshake.test.ts b/test/xx-fallback-handshake.test.ts index 130a8ff..7eb7e28 100644 --- a/test/xx-fallback-handshake.test.ts +++ b/test/xx-fallback-handshake.test.ts @@ -48,10 +48,10 @@ describe("XX Fallback Handshake", () => { }); const handshakeInit = - new Handshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB, ephemeralKeys, initialMsg); + new Handshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB, initialMsg, ephemeralKeys); const handshakeResp = - new Handshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA, ephemeralKeys, initialMsg); + new Handshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA, initialMsg); await handshakeInit.propose(); From c3ab986d3da086098da061b5ad359d85936d2670 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Tue, 7 Jan 2020 13:34:45 +0100 Subject: [PATCH 12/16] Address PR comments --- .eslintrc | 3 ++- src/@types/handshake-interface.ts | 2 +- src/crypto.ts | 6 +++--- src/handshake-ik.ts | 14 +++++++------- src/handshake-xx-fallback.ts | 12 ++++++------ src/handshake-xx.ts | 16 ++++++++-------- src/noise.ts | 24 ++++++++++++------------ test/noise.test.ts | 4 ++-- test/xx-fallback-handshake.test.ts | 6 +++--- test/xx-handshake.test.ts | 14 +++++++------- 10 files changed, 51 insertions(+), 50 deletions(-) diff --git a/.eslintrc b/.eslintrc index a621e68..46054ea 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,6 +15,7 @@ "@typescript-eslint/indent": ["error", 2], "@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/interface-name-prefix": "off", "no-console": "warn" } -} \ No newline at end of file +} diff --git a/src/@types/handshake-interface.ts b/src/@types/handshake-interface.ts index 5c152e5..d92b9ff 100644 --- a/src/@types/handshake-interface.ts +++ b/src/@types/handshake-interface.ts @@ -1,7 +1,7 @@ import {bytes} from "./basic"; import {NoiseSession} from "./handshake"; -export interface HandshakeInterface { +export interface IHandshake { session: NoiseSession; encrypt(plaintext: bytes, session: NoiseSession): bytes; decrypt(ciphertext: bytes, session: NoiseSession): bytes; diff --git a/src/crypto.ts b/src/crypto.ts index eff1494..1c63b6d 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -1,5 +1,5 @@ import { Buffer } from "buffer"; -import {HandshakeInterface} from "./@types/handshake-interface"; +import {IHandshake} from "./@types/handshake-interface"; interface ReturnEncryptionWrapper { (source: Iterable): AsyncIterableIterator; @@ -8,7 +8,7 @@ interface ReturnEncryptionWrapper { const maxPlaintextLength = 65519; // Returns generator that encrypts payload from the user -export function encryptStream(handshake: HandshakeInterface): ReturnEncryptionWrapper { +export function encryptStream(handshake: IHandshake): ReturnEncryptionWrapper { return async function * (source) { for await (const chunk of source) { const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length); @@ -28,7 +28,7 @@ export function encryptStream(handshake: HandshakeInterface): ReturnEncryptionWr // Decrypt received payload to the user -export function decryptStream(handshake: HandshakeInterface): ReturnEncryptionWrapper { +export function decryptStream(handshake: IHandshake): ReturnEncryptionWrapper { return async function * (source) { for await (const chunk of source) { const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length); diff --git a/src/handshake-ik.ts b/src/handshake-ik.ts index 5f173f2..30afa1a 100644 --- a/src/handshake-ik.ts +++ b/src/handshake-ik.ts @@ -3,17 +3,17 @@ import {IK} from "./handshakes/ik"; import {NoiseSession} from "./@types/handshake"; import {bytes, bytes32} from "./@types/basic"; import {KeyPair, PeerId} from "./@types/libp2p"; -import {HandshakeInterface} from "./@types/handshake-interface"; +import {IHandshake} from "./@types/handshake-interface"; import {Buffer} from "buffer"; -export class Handshake implements HandshakeInterface { +export class IKHandshake implements IHandshake { public isInitiator: boolean; public session: NoiseSession; private libp2pPrivateKey: bytes; private libp2pPublicKey: bytes; private prologue: bytes32; - private staticKeys: KeyPair; + private staticKeypair: KeyPair; private connection: WrappedConnection; private remotePeer: PeerId; private ik: IK; @@ -23,7 +23,7 @@ export class Handshake implements HandshakeInterface { libp2pPrivateKey: bytes, libp2pPublicKey: bytes, prologue: bytes32, - staticKeys: KeyPair, + staticKeypair: KeyPair, connection: WrappedConnection, remotePeer: PeerId, handshake?: IK, @@ -32,7 +32,7 @@ export class Handshake implements HandshakeInterface { this.libp2pPrivateKey = libp2pPrivateKey; this.libp2pPublicKey = libp2pPublicKey; this.prologue = prologue; - this.staticKeys = staticKeys; + this.staticKeypair = staticKeypair; this.connection = connection; this.remotePeer = remotePeer; @@ -40,8 +40,8 @@ export class Handshake implements HandshakeInterface { // Dummy data // TODO: Load remote static keys if found - const remoteStaticKeys = this.staticKeys; - this.session = this.ik.initSession(this.isInitiator, this.prologue, this.staticKeys, remoteStaticKeys.publicKey); + const remoteStaticKeys = this.staticKeypair; + this.session = this.ik.initSession(this.isInitiator, this.prologue, this.staticKeypair, remoteStaticKeys.publicKey); } public decrypt(ciphertext: Buffer, session: NoiseSession): Buffer { diff --git a/src/handshake-xx-fallback.ts b/src/handshake-xx-fallback.ts index a7c0788..8f9a7d6 100644 --- a/src/handshake-xx-fallback.ts +++ b/src/handshake-xx-fallback.ts @@ -1,6 +1,6 @@ import { Buffer } from "buffer"; -import { Handshake as XXHandshake } from "./handshake-xx"; +import { XXHandshake } from "./handshake-xx"; import { XX } from "./handshakes/xx"; import { KeyPair, PeerId } from "./@types/libp2p"; import { bytes, bytes32 } from "./@types/basic"; @@ -15,7 +15,7 @@ import { logger } from "./logger"; import { WrappedConnection } from "./noise"; import {decode0, decode1, encode1} from "./encoder"; -export class Handshake extends XXHandshake { +export class XXFallbackHandshake extends XXHandshake { private ephemeralKeys?: KeyPair; private initialMsg: bytes; @@ -24,14 +24,14 @@ export class Handshake extends XXHandshake { libp2pPrivateKey: bytes, libp2pPublicKey: bytes, prologue: bytes32, - staticKeys: KeyPair, + staticKeypair: KeyPair, connection: WrappedConnection, remotePeer: PeerId, initialMsg: bytes, ephemeralKeys?: KeyPair, handshake?: XX, ) { - super(isInitiator, libp2pPrivateKey, libp2pPublicKey, prologue, staticKeys, connection, remotePeer, handshake); + super(isInitiator, libp2pPrivateKey, libp2pPublicKey, prologue, staticKeypair, connection, remotePeer, handshake); if (ephemeralKeys) { this.ephemeralKeys = ephemeralKeys; } @@ -59,7 +59,7 @@ export class Handshake extends XXHandshake { public async exchange(): Promise { if (this.isInitiator) { logger('XX Fallback Stage 1 - Initiator waiting to receive first message from responder...'); - const receivedMessageBuffer = decode1((await this.connection.readLP()).slice()); + const receivedMessageBuffer = decode1((await this.connection.readLP())); // const receivedMessageBuffer = decode1(this.initialMsg); logger("Initiator receivedMessageBuffer in stage 1", receivedMessageBuffer); const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer); @@ -74,7 +74,7 @@ export class Handshake extends XXHandshake { logger("All good with the signature!"); } else { logger('XX Fallback Stage 1 - Responder sending out first message with signed payload and static key.'); - const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeys.publicKey)); + const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeypair.publicKey)); const signedEarlyDataPayload = signEarlyDataPayload(this.libp2pPrivateKey, Buffer.alloc(0)); const handshakePayload = await createHandshakePayload( this.libp2pPublicKey, diff --git a/src/handshake-xx.ts b/src/handshake-xx.ts index f5fd353..9dd8788 100644 --- a/src/handshake-xx.ts +++ b/src/handshake-xx.ts @@ -4,7 +4,7 @@ import { XX } from "./handshakes/xx"; import { KeyPair, PeerId } from "./@types/libp2p"; import { bytes, bytes32 } from "./@types/basic"; import { NoiseSession } from "./@types/handshake"; -import {HandshakeInterface} from "./@types/handshake-interface"; +import {IHandshake} from "./@types/handshake-interface"; import { createHandshakePayload, getHandshakePayload, @@ -16,7 +16,7 @@ import { logger } from "./logger"; import { decode0, decode1, encode0, encode1 } from "./encoder"; import { WrappedConnection } from "./noise"; -export class Handshake implements HandshakeInterface { +export class XXHandshake implements IHandshake { public isInitiator: boolean; public session: NoiseSession; @@ -24,7 +24,7 @@ export class Handshake implements HandshakeInterface { protected xx: XX; protected libp2pPrivateKey: bytes; protected libp2pPublicKey: bytes; - protected staticKeys: KeyPair; + protected staticKeypair: KeyPair; protected remotePeer: PeerId; private prologue: bytes32; @@ -34,7 +34,7 @@ export class Handshake implements HandshakeInterface { libp2pPrivateKey: bytes, libp2pPublicKey: bytes, prologue: bytes32, - staticKeys: KeyPair, + staticKeypair: KeyPair, connection: WrappedConnection, remotePeer: PeerId, handshake?: XX, @@ -43,12 +43,12 @@ export class Handshake implements HandshakeInterface { this.libp2pPrivateKey = libp2pPrivateKey; this.libp2pPublicKey = libp2pPublicKey; this.prologue = prologue; - this.staticKeys = staticKeys; + this.staticKeypair = staticKeypair; this.connection = connection; this.remotePeer = remotePeer; this.xx = handshake || new XX(); - this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeys); + this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeypair); } // stage 0 @@ -83,7 +83,7 @@ export class Handshake implements HandshakeInterface { logger("All good with the signature!"); } else { logger('Stage 1 - Responder sending out first message with signed payload and static key.'); - const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeys.publicKey)); + const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeypair.publicKey)); const signedEarlyDataPayload = signEarlyDataPayload(this.libp2pPrivateKey, Buffer.alloc(0)); const handshakePayload = await createHandshakePayload( this.libp2pPublicKey, @@ -102,7 +102,7 @@ export class Handshake implements HandshakeInterface { public async finish(earlyData?: bytes): Promise { if (this.isInitiator) { logger('Stage 2 - Initiator sending third handshake message.'); - const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeys.publicKey)); + const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeypair.publicKey)); const signedEarlyDataPayload = signEarlyDataPayload(this.libp2pPrivateKey, earlyData || Buffer.alloc(0)); const handshakePayload = await createHandshakePayload( this.libp2pPublicKey, diff --git a/src/noise.ts b/src/noise.ts index 249e76d..0b9a3ef 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -6,16 +6,16 @@ import ensureBuffer from 'it-buffer'; import pipe from 'it-pipe'; import lp from 'it-length-prefixed'; -import { Handshake as XX } from "./handshake-xx"; -import { Handshake as IK } from "./handshake-ik"; -import { Handshake as XXFallback } from "./handshake-xx-fallback"; +import { XXHandshake } from "./handshake-xx"; +import { IKHandshake } from "./handshake-ik"; +import { XXFallbackHandshake } from "./handshake-xx-fallback"; import { generateKeypair } from "./utils"; import { uint16BEDecode, uint16BEEncode } from "./encoder"; import { decryptStream, encryptStream } from "./crypto"; import { bytes } from "./@types/basic"; import { NoiseConnection, PeerId, KeyPair, SecureOutbound } from "./@types/libp2p"; import { Duplex } from "./@types/it-pair"; -import {HandshakeInterface} from "./@types/handshake-interface"; +import {IHandshake} from "./@types/handshake-interface"; export type WrappedConnection = ReturnType; @@ -105,7 +105,7 @@ export class Noise implements NoiseConnection { * @param libp2pPublicKey * @param remotePeer */ - private async performHandshake(params: HandshakeParams): Promise { + private async performHandshake(params: HandshakeParams): Promise { // TODO: Implement noise pipes if (false) { @@ -127,10 +127,10 @@ export class Noise implements NoiseConnection { params: HandshakeParams, ephemeralKeys: KeyPair, initialMsg: bytes, - ): Promise { + ): Promise { const { isInitiator, libp2pPublicKey, remotePeer, connection } = params; const handshake = - new XXFallback(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer, initialMsg, ephemeralKeys); + new XXFallbackHandshake(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer, initialMsg, ephemeralKeys); try { await handshake.propose(); @@ -145,9 +145,9 @@ export class Noise implements NoiseConnection { private async performXXHandshake( params: HandshakeParams, - ): Promise { + ): Promise { const { isInitiator, libp2pPublicKey, remotePeer, connection } = params; - const handshake = new XX(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer); + const handshake = new XXHandshake(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer); try { await handshake.propose(); @@ -162,9 +162,9 @@ export class Noise implements NoiseConnection { private async performIKHandshake( params: HandshakeParams, - ): Promise { + ): Promise { const { isInitiator, libp2pPublicKey, remotePeer, connection } = params; - const handshake = new IK(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer); + const handshake = new IKHandshake(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer); // TODO @@ -173,7 +173,7 @@ export class Noise implements NoiseConnection { private async createSecureConnection( connection: WrappedConnection, - handshake: HandshakeInterface, + handshake: IHandshake, ): Promise { // Create encryption box/unbox wrapper const [secure, user] = DuplexPair(); diff --git a/test/noise.test.ts b/test/noise.test.ts index 00297a0..34617c1 100644 --- a/test/noise.test.ts +++ b/test/noise.test.ts @@ -5,7 +5,7 @@ import { Noise } from "../src"; import {createPeerIdsFromFixtures} from "./fixtures/peer"; import Wrap from "it-pb-rpc"; import { random } from "bcrypto"; -import {Handshake} from "../src/handshake-xx"; +import {XXHandshake} from "../src/handshake-xx"; import { createHandshakePayload, generateKeypair, @@ -61,7 +61,7 @@ describe("Noise", () => { const xx = new XX(); const { privateKey: libp2pPrivKey, publicKey: libp2pPubKey } = getKeyPairFromPeerId(remotePeer); - const handshake = new Handshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, xx); + const handshake = new XXHandshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, xx); let receivedMessageBuffer = decode0((await wrapped.readLP()).slice()); // The first handshake message contains the initiator's ephemeral public key diff --git a/test/xx-fallback-handshake.test.ts b/test/xx-fallback-handshake.test.ts index 7eb7e28..78bea5f 100644 --- a/test/xx-fallback-handshake.test.ts +++ b/test/xx-fallback-handshake.test.ts @@ -9,7 +9,7 @@ import { signPayload } from "../src/utils"; import {generateEd25519Keys, getKeyPairFromPeerId} from "./utils"; -import {Handshake} from "../src/handshake-xx-fallback"; +import {XXFallbackHandshake} from "../src/handshake-xx-fallback"; import {createPeerIdsFromFixtures} from "./fixtures/peer"; import {assert} from "chai"; import {encode0, encode1} from "../src/encoder"; @@ -48,10 +48,10 @@ describe("XX Fallback Handshake", () => { }); const handshakeInit = - new Handshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB, initialMsg, ephemeralKeys); + new XXFallbackHandshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB, initialMsg, ephemeralKeys); const handshakeResp = - new Handshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA, initialMsg); + new XXFallbackHandshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA, initialMsg); await handshakeInit.propose(); diff --git a/test/xx-handshake.test.ts b/test/xx-handshake.test.ts index e1f2cb2..3b651f8 100644 --- a/test/xx-handshake.test.ts +++ b/test/xx-handshake.test.ts @@ -3,7 +3,7 @@ import Duplex from 'it-pair/duplex'; import {Buffer} from "buffer"; import Wrap from "it-pb-rpc"; -import {Handshake} from "../src/handshake-xx"; +import {XXHandshake} from "../src/handshake-xx"; import {generateKeypair} from "../src/utils"; import {createPeerIdsFromFixtures} from "./fixtures/peer"; import {getKeyPairFromPeerId} from "./utils"; @@ -27,10 +27,10 @@ describe("XX Handshake", () => { const staticKeysResponder = generateKeypair(); const { privateKey: initiatorPrivKey, publicKey: initiatorPubKey } = getKeyPairFromPeerId(peerA); - const handshakeInitator = new Handshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB); + const handshakeInitator = new XXHandshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB); const { privateKey: responderPrivKey, publicKey: responderPubKey } = getKeyPairFromPeerId(peerB); - const handshakeResponder = new Handshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA); + const handshakeResponder = new XXHandshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA); await handshakeInitator.propose(); await handshakeResponder.propose(); @@ -72,10 +72,10 @@ describe("XX Handshake", () => { const staticKeysResponder = generateKeypair(); const { privateKey: initiatorPrivKey, publicKey: initiatorPubKey } = getKeyPairFromPeerId(peerA); - const handshakeInitator = new Handshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, fakePeer); + const handshakeInitator = new XXHandshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, fakePeer); const { privateKey: responderPrivKey, publicKey: responderPubKey } = getKeyPairFromPeerId(peerB); - const handshakeResponder = new Handshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA); + const handshakeResponder = new XXHandshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA); await handshakeInitator.propose(); await handshakeResponder.propose(); @@ -100,10 +100,10 @@ describe("XX Handshake", () => { const staticKeysResponder = generateKeypair(); const { privateKey: initiatorPrivKey, publicKey: initiatorPubKey } = getKeyPairFromPeerId(peerA); - const handshakeInitator = new Handshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB); + const handshakeInitator = new XXHandshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB); const { privateKey: responderPrivKey, publicKey: responderPubKey } = getKeyPairFromPeerId(peerB); - const handshakeResponder = new Handshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, fakePeer); + const handshakeResponder = new XXHandshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, fakePeer); await handshakeInitator.propose(); await handshakeResponder.propose(); From 219553c7f58df972618ec9485495ac86d5bdac00 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Fri, 10 Jan 2020 21:25:18 +0100 Subject: [PATCH 13/16] Fix test --- src/handshake-xx-fallback.ts | 3 +-- test/xx-fallback-handshake.test.ts | 24 ++++++++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/handshake-xx-fallback.ts b/src/handshake-xx-fallback.ts index 8f9a7d6..1db6f2b 100644 --- a/src/handshake-xx-fallback.ts +++ b/src/handshake-xx-fallback.ts @@ -59,8 +59,7 @@ export class XXFallbackHandshake extends XXHandshake { public async exchange(): Promise { if (this.isInitiator) { logger('XX Fallback Stage 1 - Initiator waiting to receive first message from responder...'); - const receivedMessageBuffer = decode1((await this.connection.readLP())); - // const receivedMessageBuffer = decode1(this.initialMsg); + const receivedMessageBuffer = decode1(this.initialMsg); logger("Initiator receivedMessageBuffer in stage 1", receivedMessageBuffer); const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer); logger('XX Fallback Stage 1 - Initiator received the message. Got remote\'s static key.'); diff --git a/test/xx-fallback-handshake.test.ts b/test/xx-fallback-handshake.test.ts index 78bea5f..028f425 100644 --- a/test/xx-fallback-handshake.test.ts +++ b/test/xx-fallback-handshake.test.ts @@ -12,7 +12,8 @@ import {generateEd25519Keys, getKeyPairFromPeerId} from "./utils"; import {XXFallbackHandshake} from "../src/handshake-xx-fallback"; import {createPeerIdsFromFixtures} from "./fixtures/peer"; import {assert} from "chai"; -import {encode0, encode1} from "../src/encoder"; +import {decode1, encode0, encode1} from "../src/encoder"; +import {XX} from "../src/handshakes/xx"; describe("XX Fallback Handshake", () => { let peerA, peerB, fakePeer; @@ -21,7 +22,7 @@ describe("XX Fallback Handshake", () => { [peerA, peerB] = await createPeerIdsFromFixtures(2); }); - it("should make handshake with received ephemeral key (from initial IK message)", async () => { + it("should test that both parties can fallback to XX and finish handshake", async () => { try { const duplex = Duplex(); const connectionFrom = Wrap(duplex[0]); @@ -35,29 +36,32 @@ describe("XX Fallback Handshake", () => { const {privateKey: initiatorPrivKey, publicKey: initiatorPubKey} = getKeyPairFromPeerId(peerA); const {privateKey: responderPrivKey, publicKey: responderPubKey} = getKeyPairFromPeerId(peerB); + // Initial msg for responder is IK first message from initiator const signedPayload = signPayload(initiatorPrivKey, getHandshakePayload(staticKeysInitiator.publicKey)); const handshakePayload = await createHandshakePayload( initiatorPubKey, initiatorPrivKey, signedPayload, ); - const initialMsg = encode0({ + const initialMsgR = encode0({ ne: ephemeralKeys.publicKey, ns: Buffer.alloc(0), ciphertext: handshakePayload, }); - const handshakeInit = - new XXFallbackHandshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB, initialMsg, ephemeralKeys); - const handshakeResp = - new XXFallbackHandshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA, initialMsg); + new XXFallbackHandshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA, initialMsgR); + await handshakeResp.propose(); + await handshakeResp.exchange(); + + // Initial message for initiator is XX Message B from responder + // This is the point where initiator falls back from IK + const initialMsgI = await connectionFrom.readLP(); + const handshakeInit = + new XXFallbackHandshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB, initialMsgI, ephemeralKeys); await handshakeInit.propose(); - await handshakeResp.propose(); - - await handshakeResp.exchange(); await handshakeInit.exchange(); await handshakeInit.finish(); From ad45be5e7bcbc8b78fadc7a1700452e8204de3c4 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Sat, 11 Jan 2020 20:20:57 +0100 Subject: [PATCH 14/16] Fix merging updated payload signing --- src/handshake-ik.ts | 9 +++------ src/handshake-xx-fallback.ts | 17 +++-------------- src/handshake-xx.ts | 4 +--- src/noise.ts | 15 +++++++-------- test/noise.test.ts | 7 +++---- test/xx-fallback-handshake.test.ts | 21 +++++---------------- test/xx-handshake.test.ts | 3 +-- 7 files changed, 23 insertions(+), 53 deletions(-) diff --git a/src/handshake-ik.ts b/src/handshake-ik.ts index 30afa1a..f13e711 100644 --- a/src/handshake-ik.ts +++ b/src/handshake-ik.ts @@ -10,8 +10,7 @@ export class IKHandshake implements IHandshake { public isInitiator: boolean; public session: NoiseSession; - private libp2pPrivateKey: bytes; - private libp2pPublicKey: bytes; + private payload: bytes; private prologue: bytes32; private staticKeypair: KeyPair; private connection: WrappedConnection; @@ -20,8 +19,7 @@ export class IKHandshake implements IHandshake { constructor( isInitiator: boolean, - libp2pPrivateKey: bytes, - libp2pPublicKey: bytes, + payload: bytes, prologue: bytes32, staticKeypair: KeyPair, connection: WrappedConnection, @@ -29,8 +27,7 @@ export class IKHandshake implements IHandshake { handshake?: IK, ) { this.isInitiator = isInitiator; - this.libp2pPrivateKey = libp2pPrivateKey; - this.libp2pPublicKey = libp2pPublicKey; + this.payload = payload; this.prologue = prologue; this.staticKeypair = staticKeypair; this.connection = connection; diff --git a/src/handshake-xx-fallback.ts b/src/handshake-xx-fallback.ts index 1db6f2b..223ee2b 100644 --- a/src/handshake-xx-fallback.ts +++ b/src/handshake-xx-fallback.ts @@ -21,8 +21,7 @@ export class XXFallbackHandshake extends XXHandshake { constructor( isInitiator: boolean, - libp2pPrivateKey: bytes, - libp2pPublicKey: bytes, + payload: bytes, prologue: bytes32, staticKeypair: KeyPair, connection: WrappedConnection, @@ -31,7 +30,7 @@ export class XXFallbackHandshake extends XXHandshake { ephemeralKeys?: KeyPair, handshake?: XX, ) { - super(isInitiator, libp2pPrivateKey, libp2pPublicKey, prologue, staticKeypair, connection, remotePeer, handshake); + super(isInitiator, payload, prologue, staticKeypair, connection, remotePeer, handshake); if (ephemeralKeys) { this.ephemeralKeys = ephemeralKeys; } @@ -60,7 +59,6 @@ export class XXFallbackHandshake extends XXHandshake { if (this.isInitiator) { logger('XX Fallback Stage 1 - Initiator waiting to receive first message from responder...'); const receivedMessageBuffer = decode1(this.initialMsg); - logger("Initiator receivedMessageBuffer in stage 1", receivedMessageBuffer); const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer); logger('XX Fallback Stage 1 - Initiator received the message. Got remote\'s static key.'); @@ -73,16 +71,7 @@ export class XXFallbackHandshake extends XXHandshake { logger("All good with the signature!"); } else { logger('XX Fallback Stage 1 - Responder sending out first message with signed payload and static key.'); - const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeypair.publicKey)); - const signedEarlyDataPayload = signEarlyDataPayload(this.libp2pPrivateKey, Buffer.alloc(0)); - const handshakePayload = await createHandshakePayload( - this.libp2pPublicKey, - this.libp2pPrivateKey, - signedPayload, - signedEarlyDataPayload, - ); - - const messageBuffer = this.xx.sendMessage(this.session, handshakePayload); + const messageBuffer = this.xx.sendMessage(this.session, this.payload); this.connection.writeLP(encode1(messageBuffer)); logger('XX Fallback Stage 1 - Responder sent the second handshake message with signed payload.') } diff --git a/src/handshake-xx.ts b/src/handshake-xx.ts index fff6698..7f6f1bf 100644 --- a/src/handshake-xx.ts +++ b/src/handshake-xx.ts @@ -16,15 +16,13 @@ export class XXHandshake implements IHandshake { public isInitiator: boolean; public session: NoiseSession; + protected payload: bytes; protected connection: WrappedConnection; protected xx: XX; - protected libp2pPrivateKey: bytes; - protected libp2pPublicKey: bytes; protected staticKeypair: KeyPair; protected remotePeer: PeerId; private prologue: bytes32; - private payload: bytes; constructor( isInitiator: boolean, diff --git a/src/noise.ts b/src/noise.ts index 5c904f0..6f99fab 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -22,7 +22,7 @@ export type WrappedConnection = ReturnType; type HandshakeParams = { connection: WrappedConnection; isInitiator: boolean; - libp2pPublicKey: bytes; + localPeer: PeerId; remotePeer: PeerId; }; @@ -30,7 +30,6 @@ export class Noise implements NoiseConnection { public protocol = "/noise"; private readonly prologue = Buffer.from(this.protocol); - private readonly privateKey: bytes; private readonly staticKeys: KeyPair; private readonly earlyData?: bytes; @@ -127,9 +126,9 @@ export class Noise implements NoiseConnection { ephemeralKeys: KeyPair, initialMsg: bytes, ): Promise { - const { isInitiator, libp2pPublicKey, remotePeer, connection } = params; + const { isInitiator, remotePeer, connection } = params; const handshake = - new XXFallbackHandshake(isInitiator, payload, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer, initialMsg, ephemeralKeys); + new XXFallbackHandshake(isInitiator, payload, this.prologue, this.staticKeys, connection, remotePeer, initialMsg, ephemeralKeys); try { await handshake.propose(); @@ -146,8 +145,8 @@ export class Noise implements NoiseConnection { params: HandshakeParams, payload: bytes, ): Promise { - const { isInitiator, libp2pPublicKey, remotePeer, connection } = params; - const handshake = new XXHandshake(isInitiator, payload, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer); + const { isInitiator, remotePeer, connection } = params; + const handshake = new XXHandshake(isInitiator, payload, this.prologue, this.staticKeys, connection, remotePeer); try { await handshake.propose(); @@ -164,8 +163,8 @@ export class Noise implements NoiseConnection { params: HandshakeParams, payload: bytes, ): Promise { - const { isInitiator, libp2pPublicKey, remotePeer, connection } = params; - const handshake = new IKHandshake(isInitiator, payload, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer); + const { isInitiator, localPeer, remotePeer, connection } = params; + const handshake = new IKHandshake(isInitiator, payload, this.prologue, this.staticKeys, connection, remotePeer); // TODO diff --git a/test/noise.test.ts b/test/noise.test.ts index 7a94809..dcf790f 100644 --- a/test/noise.test.ts +++ b/test/noise.test.ts @@ -57,9 +57,8 @@ describe("Noise", () => { const staticKeys = generateKeypair(); const xx = new XX(); - const handshake = new XXHandshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, xx); const payload = await getPayload(remotePeer, staticKeys.publicKey); - const handshake = new Handshake(false, payload, prologue, staticKeys, wrapped, localPeer, xx); + const handshake = new XXHandshake(false, payload, prologue, staticKeys, wrapped, localPeer, xx); let receivedMessageBuffer = decode0((await wrapped.readLP()).slice()); // The first handshake message contains the initiator's ephemeral public key @@ -67,9 +66,9 @@ describe("Noise", () => { xx.recvMessage(handshake.session, receivedMessageBuffer); // Stage 1 - const { privateKey: libp2pPrivKey, publicKey: libp2pPubKey } = getKeyPairFromPeerId(remotePeer); + const { publicKey: libp2pPubKey } = getKeyPairFromPeerId(remotePeer); const signedPayload = await signPayload(remotePeer, getHandshakePayload(staticKeys.publicKey)); - const handshakePayload = await createHandshakePayload(libp2pPubKey, libp2pPrivKey, signedPayload); + const handshakePayload = await createHandshakePayload(libp2pPubKey, signedPayload); const messageBuffer = xx.sendMessage(handshake.session, handshakePayload); wrapped.writeLP(encode1(messageBuffer)); diff --git a/test/xx-fallback-handshake.test.ts b/test/xx-fallback-handshake.test.ts index 028f425..18bfeba 100644 --- a/test/xx-fallback-handshake.test.ts +++ b/test/xx-fallback-handshake.test.ts @@ -3,17 +3,13 @@ import {Buffer} from "buffer"; import Duplex from 'it-pair/duplex'; import { - createHandshakePayload, generateKeypair, - getHandshakePayload, - signPayload + getPayload, } from "../src/utils"; -import {generateEd25519Keys, getKeyPairFromPeerId} from "./utils"; import {XXFallbackHandshake} from "../src/handshake-xx-fallback"; import {createPeerIdsFromFixtures} from "./fixtures/peer"; import {assert} from "chai"; import {decode1, encode0, encode1} from "../src/encoder"; -import {XX} from "../src/handshakes/xx"; describe("XX Fallback Handshake", () => { let peerA, peerB, fakePeer; @@ -33,24 +29,17 @@ describe("XX Fallback Handshake", () => { const staticKeysResponder = generateKeypair(); const ephemeralKeys = generateKeypair(); - const {privateKey: initiatorPrivKey, publicKey: initiatorPubKey} = getKeyPairFromPeerId(peerA); - const {privateKey: responderPrivKey, publicKey: responderPubKey} = getKeyPairFromPeerId(peerB); - // Initial msg for responder is IK first message from initiator - const signedPayload = signPayload(initiatorPrivKey, getHandshakePayload(staticKeysInitiator.publicKey)); - const handshakePayload = await createHandshakePayload( - initiatorPubKey, - initiatorPrivKey, - signedPayload, - ); + const handshakePayload = await getPayload(peerA, staticKeysInitiator.publicKey); const initialMsgR = encode0({ ne: ephemeralKeys.publicKey, ns: Buffer.alloc(0), ciphertext: handshakePayload, }); + const respPayload = await getPayload(peerB, staticKeysResponder.publicKey); const handshakeResp = - new XXFallbackHandshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA, initialMsgR); + new XXFallbackHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, peerA, initialMsgR); await handshakeResp.propose(); await handshakeResp.exchange(); @@ -59,7 +48,7 @@ describe("XX Fallback Handshake", () => { // This is the point where initiator falls back from IK const initialMsgI = await connectionFrom.readLP(); const handshakeInit = - new XXFallbackHandshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB, initialMsgI, ephemeralKeys); + new XXFallbackHandshake(true, handshakePayload, prologue, staticKeysInitiator, connectionFrom, peerB, initialMsgI, ephemeralKeys); await handshakeInit.propose(); await handshakeInit.exchange(); diff --git a/test/xx-handshake.test.ts b/test/xx-handshake.test.ts index 569c35b..08851a4 100644 --- a/test/xx-handshake.test.ts +++ b/test/xx-handshake.test.ts @@ -4,9 +4,8 @@ import {Buffer} from "buffer"; import Wrap from "it-pb-rpc"; import {XXHandshake} from "../src/handshake-xx"; -import {generateKeypair} from "../src/utils"; +import {generateKeypair, getPayload} from "../src/utils"; import {createPeerIdsFromFixtures} from "./fixtures/peer"; -import {getKeyPairFromPeerId} from "./utils"; describe("XX Handshake", () => { From 1b89efd2889f8e50002dffe50502a43c1bd4ca15 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Sat, 11 Jan 2020 20:23:33 +0100 Subject: [PATCH 15/16] Force interface name prefix --- .eslintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 46054ea..b182f12 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,7 +15,7 @@ "@typescript-eslint/indent": ["error", 2], "@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/interface-name-prefix": "off", + "@typescript-eslint/interface-name-prefix": ["error", { "prefixWithI": "always" }], "no-console": "warn" } } From dd94793e23ee32a3c4b611a084c202eb02f516ed Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Sat, 11 Jan 2020 20:27:26 +0100 Subject: [PATCH 16/16] Fix eslint --- src/@types/handshake.ts | 2 +- src/@types/libp2p.ts | 4 ++-- src/crypto.ts | 6 +++--- src/handshake-xx-fallback.ts | 4 ---- src/noise.ts | 6 +++--- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/@types/handshake.ts b/src/@types/handshake.ts index 265f769..cb67eeb 100644 --- a/src/@types/handshake.ts +++ b/src/@types/handshake.ts @@ -3,7 +3,7 @@ import {KeyPair} from "./libp2p"; export type Hkdf = [bytes, bytes, bytes]; -export interface MessageBuffer { +export type MessageBuffer = { ne: bytes32; ns: bytes; ciphertext: bytes; diff --git a/src/@types/libp2p.ts b/src/@types/libp2p.ts index 58ecd87..8f0ac27 100644 --- a/src/@types/libp2p.ts +++ b/src/@types/libp2p.ts @@ -1,7 +1,7 @@ import { bytes, bytes32 } from "./basic"; import { Duplex } from "it-pair"; -export interface KeyPair { +export type KeyPair = { publicKey: bytes32; privateKey: bytes32; } @@ -18,7 +18,7 @@ export type PeerId = { marshalPrivKey(): bytes; }; -export interface NoiseConnection { +export interface INoiseConnection { remoteEarlyData?(): bytes; secureOutbound(localPeer: PeerId, insecure: any, remotePeer: PeerId): Promise; secureInbound(localPeer: PeerId, insecure: any, remotePeer: PeerId): Promise; diff --git a/src/crypto.ts b/src/crypto.ts index 1c63b6d..83508ec 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -1,14 +1,14 @@ import { Buffer } from "buffer"; import {IHandshake} from "./@types/handshake-interface"; -interface ReturnEncryptionWrapper { +interface IReturnEncryptionWrapper { (source: Iterable): AsyncIterableIterator; } const maxPlaintextLength = 65519; // Returns generator that encrypts payload from the user -export function encryptStream(handshake: IHandshake): ReturnEncryptionWrapper { +export function encryptStream(handshake: IHandshake): IReturnEncryptionWrapper { return async function * (source) { for await (const chunk of source) { const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length); @@ -28,7 +28,7 @@ export function encryptStream(handshake: IHandshake): ReturnEncryptionWrapper { // Decrypt received payload to the user -export function decryptStream(handshake: IHandshake): ReturnEncryptionWrapper { +export function decryptStream(handshake: IHandshake): IReturnEncryptionWrapper { return async function * (source) { for await (const chunk of source) { const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length); diff --git a/src/handshake-xx-fallback.ts b/src/handshake-xx-fallback.ts index 223ee2b..c01539b 100644 --- a/src/handshake-xx-fallback.ts +++ b/src/handshake-xx-fallback.ts @@ -5,10 +5,6 @@ import { XX } from "./handshakes/xx"; import { KeyPair, PeerId } from "./@types/libp2p"; import { bytes, bytes32 } from "./@types/basic"; import { - createHandshakePayload, - getHandshakePayload, - signEarlyDataPayload, - signPayload, verifySignedPayload, } from "./utils"; import { logger } from "./logger"; diff --git a/src/noise.ts b/src/noise.ts index 6f99fab..676beb3 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -13,7 +13,7 @@ import { generateKeypair, getPayload } from "./utils"; import { uint16BEDecode, uint16BEEncode } from "./encoder"; import { decryptStream, encryptStream } from "./crypto"; import { bytes } from "./@types/basic"; -import { NoiseConnection, PeerId, KeyPair, SecureOutbound } from "./@types/libp2p"; +import { INoiseConnection, PeerId, KeyPair, SecureOutbound } from "./@types/libp2p"; import { Duplex } from "./@types/it-pair"; import {IHandshake} from "./@types/handshake-interface"; @@ -26,7 +26,7 @@ type HandshakeParams = { remotePeer: PeerId; }; -export class Noise implements NoiseConnection { +export class Noise implements INoiseConnection { public protocol = "/noise"; private readonly prologue = Buffer.from(this.protocol); @@ -163,7 +163,7 @@ export class Noise implements NoiseConnection { params: HandshakeParams, payload: bytes, ): Promise { - const { isInitiator, localPeer, remotePeer, connection } = params; + const { isInitiator, remotePeer, connection } = params; const handshake = new IKHandshake(isInitiator, payload, this.prologue, this.staticKeys, connection, remotePeer); // TODO