From fe706fbd71d4c5f4f427d2939d99f60708d5ff3f Mon Sep 17 00:00:00 2001 From: morrigan Date: Wed, 6 Nov 2019 13:24:30 +0100 Subject: [PATCH] Create part of XX test and update code accordingly --- package.json | 7 ++-- payload.proto | 9 +++++ src/xx.ts | 37 ++++++++++++--------- test/xx.test.ts | 50 +++++++++++++++++++++++++++- yarn.lock | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 172 insertions(+), 18 deletions(-) create mode 100644 payload.proto diff --git a/package.json b/package.json index 8775f27..9734daf 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "devDependencies": { "@babel/cli": "^7.6.4", "@babel/core": "^7.6.4", + "@babel/plugin-proposal-object-rest-spread": "^7.6.2", "@babel/plugin-transform-runtime": "^7.6.2", "@babel/preset-env": "^7.6.3", "@babel/preset-typescript": "^7.6.0", @@ -37,13 +38,15 @@ "@babel/preset-typescript" ], "plugins": [ - "@babel/plugin-transform-runtime" + "@babel/plugin-transform-runtime", + "@babel/plugin-proposal-object-rest-spread" ] }, "dependencies": { "bcrypto": "^4.2.3", "bn.js": "^5.0.0", "buffer": "^5.4.3", - "libp2p-crypto": "^0.17.1" + "libp2p-crypto": "^0.17.1", + "protobufjs": "~6.8.8" } } diff --git a/payload.proto b/payload.proto new file mode 100644 index 0000000..765fa4d --- /dev/null +++ b/payload.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; +package pb; + +message NoiseHandshakePayload { + bytes libp2p_key = 1; + bytes noise_static_key_signature = 2; + bytes libp2p_data = 3; + bytes libp2p_data_signature = 4; +} diff --git a/src/xx.ts b/src/xx.ts index 551fcb5..30c77b4 100644 --- a/src/xx.ts +++ b/src/xx.ts @@ -55,7 +55,7 @@ export class XXHandshake { private async initializeInitiator(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32) : Promise { const name = "Noise_XX_25519_ChaChaPoly_SHA256"; const ss = await this.initializeSymmetric(name); - await this.mixHash(ss, prologue); + this.mixHash(ss, prologue); const re = Buffer.alloc(32); return { ss, s, rs, psk, re }; @@ -64,7 +64,7 @@ export class XXHandshake { private async initializeResponder(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32) : Promise { const name = "Noise_XX_25519_ChaChaPoly_SHA256"; const ss = await this.initializeSymmetric(name); - await this.mixHash(ss, prologue); + this.mixHash(ss, prologue); const re = Buffer.alloc(32); return { ss, s, rs, psk, re }; @@ -143,6 +143,7 @@ export class XXHandshake { private async initializeSymmetric(protocolName: string) : Promise { const protocolNameBytes: bytes = Buffer.from(protocolName, 'utf-8'); const h = await this.hashProtocolName(protocolNameBytes); + const ck = h; const key = this.createEmptyKey(); const cs = this.initializeKey(key); @@ -158,11 +159,11 @@ export class XXHandshake { private async hashProtocolName(protocolName: bytes) : Promise { if (protocolName.length <= 32) { - const h = Buffer.alloc(32); + let h = Buffer.alloc(32); protocolName.copy(h); return h; } else { - return await this.getHash(protocolName, Buffer.from([])); + return this.getHash(protocolName, Buffer.alloc(0)); } } @@ -178,12 +179,12 @@ export class XXHandshake { return [ k1, k2, k3 ]; } - private async mixHash(ss: SymmetricState, data: bytes) { - ss.h = await this.getHash(ss.h, data); + private mixHash(ss: SymmetricState, data: bytes) { + ss.h = this.getHash(ss.h, data); } - private async getHash(a: bytes, b: bytes) : Promise { - return await crypto.hmac.create('sha256', Buffer.from([...a, ...b])) + private getHash(a: bytes, b: bytes) : bytes32 { + return SHA256.digest(Buffer.from([...a, ...b])); } private async encryptAndHash(ss: SymmetricState, plaintext: bytes) : Promise { @@ -194,7 +195,7 @@ export class XXHandshake { ciphertext = plaintext; } - await this.mixHash(ss, ciphertext); + this.mixHash(ss, ciphertext); return ciphertext; } @@ -206,7 +207,7 @@ export class XXHandshake { plaintext = ciphertext; } - await this.mixHash(ss, ciphertext); + this.mixHash(ss, ciphertext); return plaintext; } @@ -222,7 +223,8 @@ export class XXHandshake { let ns = Buffer.alloc(0); hs.e = await this.generateKeypair(); const ne = hs.e.publicKey; - await this.mixHash(hs.ss, ne); + + this.mixHash(hs.ss, ne); const ciphertext = await this.encryptAndHash(hs.ss, payload); return {ne, ns, ciphertext}; @@ -231,7 +233,7 @@ export class XXHandshake { private async writeMessageB(hs: HandshakeState, payload: bytes) : Promise { hs.e = await this.generateKeypair(); const ne = hs.e.publicKey; - await this.mixHash(hs.ss, ne); + this.mixHash(hs.ss, ne); await this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.re)); const spk = Buffer.from(hs.s.publicKey); const ns = await this.encryptAndHash(hs.ss, spk); @@ -264,14 +266,14 @@ export class XXHandshake { private async readMessageA(hs: HandshakeState, message: MessageBuffer) : Promise { // TODO: validate public key here - await this.mixHash(hs.ss, hs.re); + this.mixHash(hs.ss, hs.re); return await this.decryptAndHash(hs.ss, message.ciphertext); } private async readMessageB(hs: HandshakeState, message: MessageBuffer) : Promise { // TODO: validate public key here - await this.mixHash(hs.ss, hs.re); + this.mixHash(hs.ss, hs.re); if (!hs.e) { throw new Error("Handshake state `e` param is missing."); } @@ -302,7 +304,12 @@ export class XXHandshake { } public async generateKeypair() : Promise { - return await crypto.keys.generateKeyPair('ed25519'); + const Ed25519PrivateKey = await crypto.keys.generateKeyPair('ed25519'); + + return { + publicKey: Ed25519PrivateKey.public.bytes, + privateKey: Ed25519PrivateKey.bytes, + } } public async initSession(initiator: boolean, prologue: bytes32, s: KeyPair, rs: bytes32) : Promise { diff --git a/test/xx.test.ts b/test/xx.test.ts index 10628b4..356293b 100644 --- a/test/xx.test.ts +++ b/test/xx.test.ts @@ -1,5 +1,7 @@ -import { expect } from "chai"; +import { expect, assert } from "chai"; import { Buffer } from 'buffer'; +import * as crypto from 'libp2p-crypto'; +import protobuf from 'protobufjs'; import { XXHandshake, KeyPair } from "../src/xx"; @@ -28,4 +30,50 @@ describe("Index", () => { expect(k2.toString('hex')).to.equal('a16ada915e551ab623f38be674bb4ef15d428ae9d80688899c9ef9b62ef208fa'); expect(k3.toString('hex')).to.equal('ff67bf9727e31b06efc203907e6786667d2c7a74ac412b4d31a80ba3fd766f68'); }) + + async function generateKeypair() { + return await crypto.keys.generateKeyPair('ed25519');; + } + + async function doHandshake() { + const xx = new XXHandshake(); + const kpInit = await xx.generateKeypair(); + const kpResp = await xx.generateKeypair(); + const payloadString = Buffer.from("noise-libp2p-static-key:"); + + // initiator setup + const libp2pInitKeys = await generateKeypair(); + const initSignedPayload = await libp2pInitKeys.sign(Buffer.concat([payloadString, kpInit.publicKey])); + + // responder setup + const libp2pRespKeys = await generateKeypair(); + const respSignedPayload = await libp2pRespKeys.sign(Buffer.concat([payloadString, kpResp.publicKey])); + + // initiator: new XX noise session + const nsInit = await xx.initSession(true, prologue, kpInit, kpResp.publicKey); + // responder: new XX noise session + const nsResp = await xx.initSession(false, prologue, kpResp, kpInit.publicKey); + + /* stage 0: initiator */ + + // initiator creates payload + const payloadProtoBuf = await protobuf.load("payload.proto"); + const NoiseHandshakePayload = payloadProtoBuf.lookupType("pb.NoiseHandshakePayload"); + const payloadInit = NoiseHandshakePayload.create({ + libp2pKey: libp2pInitKeys.bytes.toString('hex'), + noiseStaticKeySignature: initSignedPayload, + }); + + const payloadInitEnc = NoiseHandshakePayload.encode(payloadInit).finish(); + + // initiator sends message + const message = Buffer.concat([Buffer.alloc(0), payloadInitEnc]); + const messageBuffer = await xx.sendMessage(nsInit, message); + + expect(messageBuffer.ne.length).not.equal(0); + } + + it("Test handshake", async () => { + await doHandshake(); + }) }); diff --git a/yarn.lock b/yarn.lock index 4d57bf8..e656c1a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -718,6 +718,59 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= + "@types/chai@^4.2.4": version "4.2.4" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.4.tgz#8936cffad3c96ec470a2dc26a38c3ba8b9b6f619" @@ -733,11 +786,21 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== +"@types/long@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef" + integrity sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q== + "@types/mocha@^5.2.7": version "5.2.7" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ== +"@types/node@^10.1.0": + version "10.17.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.3.tgz#65a8d9a6a0f6af55595a2d0020617959130d6495" + integrity sha512-QZ9CjUB3QoA3f2afw3utKlfRPhpmufB7jC2+oDhLWnXqoyx333fhKSQDLQu2EK7OE0a15X67eYiRAaJsHXrpMA== + "@types/node@^10.12.12": version "10.17.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.2.tgz#41b5afbcde1a5a805302a4da3cf399499f1bbf64" @@ -2401,6 +2464,11 @@ log-symbols@2.2.0: dependencies: chalk "^2.0.1" +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + loose-envify@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -2982,6 +3050,25 @@ promise@~1.3.0: dependencies: is-promise "~1" +protobufjs@~6.8.8: + version "6.8.8" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.8.tgz#c8b4f1282fd7a90e6f5b109ed11c84af82908e7c" + integrity sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.0" + "@types/node" "^10.1.0" + long "^4.0.0" + protocol-buffers-schema@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.3.2.tgz#00434f608b4e8df54c59e070efeefc37fb4bb859"