From e66ee7d677ee8122e85194deace1b8f0603d8063 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Tue, 24 Dec 2019 20:45:48 +0100 Subject: [PATCH 01/10] Create handshakes dir --- src/encoder.ts | 2 +- src/handshake.ts | 2 +- src/handshakes/ik.ts | 0 src/{ => handshakes}/xx.ts | 6 +++--- test/handshakes/ik.test.ts | 0 test/{ => handshakes}/xx.test.ts | 8 ++++---- test/noise.test.ts | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 src/handshakes/ik.ts rename src/{ => handshakes}/xx.ts (98%) create mode 100644 test/handshakes/ik.test.ts rename test/{ => handshakes}/xx.test.ts (96%) diff --git a/src/encoder.ts b/src/encoder.ts index 6c4cf75..383187b 100644 --- a/src/encoder.ts +++ b/src/encoder.ts @@ -1,6 +1,6 @@ import {Buffer} from "buffer"; import {bytes} from "./@types/basic"; -import {MessageBuffer} from "./xx"; +import {MessageBuffer} from "./handshakes/xx"; export const int16BEEncode = (value, target, offset) => { target = target || Buffer.allocUnsafe(2); diff --git a/src/handshake.ts b/src/handshake.ts index cb76381..d530e05 100644 --- a/src/handshake.ts +++ b/src/handshake.ts @@ -1,7 +1,7 @@ import { Buffer } from "buffer"; import { bytes, bytes32 } from "./@types/basic"; -import { NoiseSession, XXHandshake } from "./xx"; +import { NoiseSession, XXHandshake } from "./handshakes/xx"; import { KeyPair, PeerId } from "./@types/libp2p"; import { createHandshakePayload, diff --git a/src/handshakes/ik.ts b/src/handshakes/ik.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/xx.ts b/src/handshakes/xx.ts similarity index 98% rename from src/xx.ts rename to src/handshakes/xx.ts index d4b479a..7e0c9de 100644 --- a/src/xx.ts +++ b/src/handshakes/xx.ts @@ -2,9 +2,9 @@ import { Buffer } from 'buffer'; import { AEAD, x25519, HKDF, SHA256 } from 'bcrypto'; import { BN } from 'bn.js'; -import { bytes32, uint32, uint64, bytes } from './@types/basic' -import { KeyPair } from './@types/libp2p' -import { generateKeypair } from './utils'; +import { bytes32, uint32, uint64, bytes } from '../@types/basic' +import { KeyPair } from '../@types/libp2p' +import { generateKeypair } from '../utils'; export interface MessageBuffer { ne: bytes32; diff --git a/test/handshakes/ik.test.ts b/test/handshakes/ik.test.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/xx.test.ts b/test/handshakes/xx.test.ts similarity index 96% rename from test/xx.test.ts rename to test/handshakes/xx.test.ts index 1eca6b4..4bc7e1a 100644 --- a/test/xx.test.ts +++ b/test/handshakes/xx.test.ts @@ -1,10 +1,10 @@ import { expect, assert } from "chai"; import { Buffer } from 'buffer'; -import { XXHandshake } from "../src/xx"; -import { KeyPair } from "../src/@types/libp2p"; -import { generateEd25519Keys } from "./utils"; -import {createHandshakePayload, generateKeypair, getHandshakePayload} from "../src/utils"; +import { XXHandshake } from "../../src/handshakes/xx"; +import { KeyPair } from "../../src/@types/libp2p"; +import { generateEd25519Keys } from "../utils"; +import {createHandshakePayload, generateKeypair, getHandshakePayload} from "../../src/utils"; describe("Index", () => { const prologue = Buffer.from("/noise", "utf-8"); diff --git a/test/noise.test.ts b/test/noise.test.ts index 5d68098..5c39494 100644 --- a/test/noise.test.ts +++ b/test/noise.test.ts @@ -12,7 +12,7 @@ import { signPayload } from "../src/utils"; import { decodeMessageBuffer, encodeMessageBuffer } from "../src/encoder"; -import {XXHandshake} from "../src/xx"; +import {XXHandshake} from "../src/handshakes/xx"; import {Buffer} from "buffer"; import {getKeyPairFromPeerId, getRandomBuffer} from "./utils"; From dbbf5792881073b5aa377b606404296ab01d4417 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Tue, 24 Dec 2019 20:54:45 +0100 Subject: [PATCH 02/10] Move handshake types to separate file --- src/@types/handshake.ts | 39 +++++++++++++++++++++++++++++++++++++++ src/encoder.ts | 2 +- src/handshake.ts | 5 +++-- src/handshakes/xx.ts | 36 +----------------------------------- 4 files changed, 44 insertions(+), 38 deletions(-) create mode 100644 src/@types/handshake.ts diff --git a/src/@types/handshake.ts b/src/@types/handshake.ts new file mode 100644 index 0000000..265f769 --- /dev/null +++ b/src/@types/handshake.ts @@ -0,0 +1,39 @@ +import {bytes, bytes32, uint32, uint64} from "./basic"; +import {KeyPair} from "./libp2p"; + +export type Hkdf = [bytes, bytes, bytes]; + +export interface MessageBuffer { + ne: bytes32; + ns: bytes; + ciphertext: bytes; +} + +export type CipherState = { + k: bytes32; + n: uint32; +} + +export type SymmetricState = { + cs: CipherState; + ck: bytes32; // chaining key + h: bytes32; // handshake hash +} + +export type HandshakeState = { + ss: SymmetricState; + s: KeyPair; + e?: KeyPair; + rs: bytes32; + re: bytes32; + psk: bytes32; +} + +export type NoiseSession = { + hs: HandshakeState; + h?: bytes32; + cs1?: CipherState; + cs2?: CipherState; + mc: uint64; + i: boolean; +} diff --git a/src/encoder.ts b/src/encoder.ts index 383187b..8837d4f 100644 --- a/src/encoder.ts +++ b/src/encoder.ts @@ -1,6 +1,6 @@ import {Buffer} from "buffer"; import {bytes} from "./@types/basic"; -import {MessageBuffer} from "./handshakes/xx"; +import {MessageBuffer} from "./@types/handshake"; export const int16BEEncode = (value, target, offset) => { target = target || Buffer.allocUnsafe(2); diff --git a/src/handshake.ts b/src/handshake.ts index d530e05..b2d96dc 100644 --- a/src/handshake.ts +++ b/src/handshake.ts @@ -1,8 +1,9 @@ import { Buffer } from "buffer"; -import { bytes, bytes32 } from "./@types/basic"; -import { NoiseSession, XXHandshake } from "./handshakes/xx"; +import { XXHandshake } from "./handshakes/xx"; import { KeyPair, PeerId } from "./@types/libp2p"; +import { bytes, bytes32 } from "./@types/basic"; +import { NoiseSession } from "./@types/handshake"; import { createHandshakePayload, getHandshakePayload, diff --git a/src/handshakes/xx.ts b/src/handshakes/xx.ts index 7e0c9de..63f0dad 100644 --- a/src/handshakes/xx.ts +++ b/src/handshakes/xx.ts @@ -5,42 +5,8 @@ import { BN } from 'bn.js'; import { bytes32, uint32, uint64, bytes } from '../@types/basic' import { KeyPair } from '../@types/libp2p' import { generateKeypair } from '../utils'; +import { CipherState, HandshakeState, Hkdf, MessageBuffer, NoiseSession, SymmetricState } from "../@types/handshake"; -export interface MessageBuffer { - ne: bytes32; - ns: bytes; - ciphertext: bytes; -} - -type CipherState = { - k: bytes32; - n: uint32; -} - -type SymmetricState = { - cs: CipherState; - ck: bytes32; // chaining key - h: bytes32; // handshake hash -} - -type HandshakeState = { - ss: SymmetricState; - s: KeyPair; - e?: KeyPair; - rs: bytes32; - re: bytes32; - psk: bytes32; -} - -export type NoiseSession = { - hs: HandshakeState; - h?: bytes32; - cs1?: CipherState; - cs2?: CipherState; - mc: uint64; - i: boolean; -} -export type Hkdf = [bytes, bytes, bytes]; const minNonce = 0; From 0290df86855a90b1714ea1e0b447e2b5b72d8db8 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Tue, 24 Dec 2019 21:15:38 +0100 Subject: [PATCH 03/10] Move common function to abstract class --- src/handshakes/abstract-handshake.ts | 60 ++++++++++++++++++++++++ src/handshakes/ik.ts | 25 ++++++++++ src/handshakes/xx.ts | 70 ++-------------------------- src/utils.ts | 17 ++++++- 4 files changed, 104 insertions(+), 68 deletions(-) create mode 100644 src/handshakes/abstract-handshake.ts diff --git a/src/handshakes/abstract-handshake.ts b/src/handshakes/abstract-handshake.ts new file mode 100644 index 0000000..e34a901 --- /dev/null +++ b/src/handshakes/abstract-handshake.ts @@ -0,0 +1,60 @@ +import {Buffer} from "buffer"; +import { AEAD, x25519, HKDF, SHA256 } from 'bcrypto'; + +import {bytes, bytes32, uint32} from "../@types/basic"; +import {CipherState, SymmetricState} from "../@types/handshake"; +import {getHkdf} from "../utils"; + +export class AbstractHandshake { + protected minNonce = 0; + + protected incrementNonce(n: uint32): uint32 { + return n + 1; + } + + protected nonceToBytes(n: uint32): bytes { + const nonce = Buffer.alloc(12); + nonce.writeUInt32LE(n, 4); + + return nonce; + } + + protected encrypt(k: bytes32, n: uint32, ad: bytes, plaintext: bytes): bytes { + const nonce = this.nonceToBytes(n); + const ctx = new AEAD(); + + ctx.init(k, nonce); + ctx.aad(ad); + ctx.encrypt(plaintext); + + // Encryption is done on the sent reference + return plaintext; + } + + + protected dh(privateKey: bytes32, publicKey: bytes32): bytes32 { + const derived = x25519.derive(publicKey, privateKey); + const result = Buffer.alloc(32); + derived.copy(result); + return result; + } + + protected mixHash(ss: SymmetricState, data: bytes): void { + ss.h = this.getHash(ss.h, data); + } + + protected getHash(a: bytes, b: bytes): bytes32 { + return SHA256.digest(Buffer.from([...a, ...b])); + } + + protected mixKey(ss: SymmetricState, ikm: bytes32): void { + const [ ck, tempK ] = getHkdf(ss.ck, ikm); + ss.cs = this.initializeKey(tempK) as CipherState; + ss.ck = ck; + } + + protected initializeKey(k: bytes32): CipherState { + const n = this.minNonce; + return { k, n }; + } +} diff --git a/src/handshakes/ik.ts b/src/handshakes/ik.ts index e69de29..bf46def 100644 --- a/src/handshakes/ik.ts +++ b/src/handshakes/ik.ts @@ -0,0 +1,25 @@ +import {Buffer} from "buffer"; + +import {CipherState, HandshakeState, MessageBuffer, SymmetricState} from "../@types/handshake"; +import {bytes, bytes32} from "../@types/basic"; +import {generateKeypair, getHkdf} from "../utils"; +import {AbstractHandshake} from "./abstract-handshake"; + + +export class IKHandshake extends AbstractHandshake { + private writeMessageA(hs: HandshakeState, payload: bytes): MessageBuffer { + hs.e = generateKeypair(); + const ne = hs.e.publicKey; + this.mixHash(hs.ss, ne); + this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs)); + const spk = Buffer.from(hs.s.publicKey); + const ns = this.encryptAndHash(hs.ss, spk); + + this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.re)); + const ciphertext = this.encryptAndHash(hs.ss, payload); + + return { ne, ns, ciphertext }; + } + + +} diff --git a/src/handshakes/xx.ts b/src/handshakes/xx.ts index 63f0dad..3273ed6 100644 --- a/src/handshakes/xx.ts +++ b/src/handshakes/xx.ts @@ -4,13 +4,12 @@ import { BN } from 'bn.js'; import { bytes32, uint32, uint64, bytes } from '../@types/basic' import { KeyPair } from '../@types/libp2p' -import { generateKeypair } from '../utils'; +import {generateKeypair, getHkdf} from '../utils'; import { CipherState, HandshakeState, Hkdf, MessageBuffer, NoiseSession, SymmetricState } from "../@types/handshake"; +import {AbstractHandshake} from "./abstract-handshake"; -const minNonce = 0; - -export class XXHandshake { +export class XXHandshake extends AbstractHandshake { private createEmptyKey(): bytes32 { return Buffer.alloc(32); } @@ -33,36 +32,6 @@ export class XXHandshake { return { ss, s, rs, psk, re }; } - private incrementNonce(n: uint32): uint32 { - return n + 1; - } - - private dh(privateKey: bytes32, publicKey: bytes32): bytes32 { - const derived = x25519.derive(publicKey, privateKey); - const result = Buffer.alloc(32); - derived.copy(result); - return result; - } - - private nonceToBytes(n: uint32): bytes { - const nonce = Buffer.alloc(12); - nonce.writeUInt32LE(n, 4); - - return nonce; - } - - private encrypt(k: bytes32, n: uint32, ad: bytes, plaintext: bytes): bytes { - const nonce = this.nonceToBytes(n); - const ctx = new AEAD(); - - ctx.init(k, nonce); - ctx.aad(ad); - ctx.encrypt(plaintext); - - // Encryption is done on the sent reference - return plaintext; - } - private decrypt(k: bytes32, n: uint32, ad: bytes, ciphertext: bytes): bytes { const nonce = this.nonceToBytes(n); const ctx = new AEAD(); @@ -81,11 +50,6 @@ export class XXHandshake { } // Cipher state related - private initializeKey(k: bytes32): CipherState { - const n = minNonce; - return { k, n }; - } - private hasKey(cs: CipherState): boolean { return !this.isEmptyKey(cs.k); } @@ -121,12 +85,6 @@ export class XXHandshake { return { cs, ck, h }; } - private mixKey(ss: SymmetricState, ikm: bytes32): void { - const [ ck, tempK ] = this.getHkdf(ss.ck, ikm); - ss.cs = this.initializeKey(tempK) as CipherState; - ss.ck = ck; - } - private hashProtocolName(protocolName: bytes): bytes32 { if (protocolName.length <= 32) { const h = Buffer.alloc(32); @@ -137,26 +95,6 @@ export class XXHandshake { } } - public getHkdf(ck: bytes32, ikm: bytes): Hkdf { - const info = Buffer.alloc(0); - const prk = HKDF.extract(SHA256, ikm, ck); - const okm = HKDF.expand(SHA256, prk, info, 96); - - const k1 = okm.slice(0, 32); - const k2 = okm.slice(32, 64); - const k3 = okm.slice(64, 96); - - return [ k1, k2, k3 ]; - } - - private mixHash(ss: SymmetricState, data: bytes) { - ss.h = this.getHash(ss.h, data); - } - - private getHash(a: bytes, b: bytes): bytes32 { - return SHA256.digest(Buffer.from([...a, ...b])); - } - private encryptAndHash(ss: SymmetricState, plaintext: bytes): bytes { let ciphertext; if (this.hasKey(ss.cs)) { @@ -182,7 +120,7 @@ export class XXHandshake { } private split (ss: SymmetricState) { - const [ tempk1, tempk2 ] = this.getHkdf(ss.ck, Buffer.alloc(0)); + const [ tempk1, tempk2 ] = getHkdf(ss.ck, Buffer.alloc(0)); const cs1 = this.initializeKey(tempk1); const cs2 = this.initializeKey(tempk2); diff --git a/src/utils.ts b/src/utils.ts index 65b7939..e749040 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,11 +1,12 @@ -import { x25519, ed25519 } from 'bcrypto'; +import { x25519, ed25519, HKDF, SHA256 } from 'bcrypto'; import protobuf from "protobufjs"; import { Buffer } from "buffer"; import PeerId from "peer-id"; import * as crypto from 'libp2p-crypto'; import { KeyPair } from "./@types/libp2p"; -import { bytes } from "./@types/basic"; +import {bytes, bytes32} from "./@types/basic"; +import {Hkdf} from "./@types/handshake"; export async function loadPayloadProto () { const payloadProtoBuf = await protobuf.load("protos/payload.proto"); @@ -88,3 +89,15 @@ export async function verifySignedPayload(noiseStaticKey: bytes, plaintext: byte throw new Error("Static key doesn't match to peer that signed payload!"); } } + +export function getHkdf(ck: bytes32, ikm: bytes): Hkdf { + const info = Buffer.alloc(0); + const prk = HKDF.extract(SHA256, ikm, ck); + const okm = HKDF.expand(SHA256, prk, info, 96); + + const k1 = okm.slice(0, 32); + const k2 = okm.slice(32, 64); + const k3 = okm.slice(64, 96); + + return [ k1, k2, k3 ]; +} From 7b9118e15b871edc5d74f7f475e024e2267a057e Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Wed, 25 Dec 2019 11:11:07 +0100 Subject: [PATCH 04/10] Move remaining functions from XX --- src/handshakes/abstract-handshake.ts | 99 ++++++++++++++++++++++++++++ src/handshakes/xx.ts | 99 ---------------------------- test/handshakes/xx.test.ts | 4 +- 3 files changed, 101 insertions(+), 101 deletions(-) diff --git a/src/handshakes/abstract-handshake.ts b/src/handshakes/abstract-handshake.ts index e34a901..8b99f74 100644 --- a/src/handshakes/abstract-handshake.ts +++ b/src/handshakes/abstract-handshake.ts @@ -8,6 +8,39 @@ import {getHkdf} from "../utils"; export class AbstractHandshake { protected minNonce = 0; + public encryptWithAd(cs: CipherState, ad: bytes, plaintext: bytes): bytes { + const e = this.encrypt(cs.k, cs.n, ad, plaintext); + this.setNonce(cs, this.incrementNonce(cs.n)); + + return e; + } + + public decryptWithAd(cs: CipherState, ad: bytes, ciphertext: bytes): bytes { + const plaintext = this.decrypt(cs.k, cs.n, ad, ciphertext); + this.setNonce(cs, this.incrementNonce(cs.n)); + + return plaintext; + } + + + // Cipher state related + protected hasKey(cs: CipherState): boolean { + return !this.isEmptyKey(cs.k); + } + + protected setNonce(cs: CipherState, nonce: uint32): void { + cs.n = nonce; + } + + protected createEmptyKey(): bytes32 { + return Buffer.alloc(32); + } + + protected isEmptyKey(k: bytes32): boolean { + const emptyKey = this.createEmptyKey(); + return emptyKey.equals(k); + } + protected incrementNonce(n: uint32): uint32 { return n + 1; } @@ -31,6 +64,41 @@ export class AbstractHandshake { return plaintext; } + protected encryptAndHash(ss: SymmetricState, plaintext: bytes): bytes { + let ciphertext; + if (this.hasKey(ss.cs)) { + ciphertext = this.encryptWithAd(ss.cs, ss.h, plaintext); + } else { + ciphertext = plaintext; + } + + this.mixHash(ss, ciphertext); + return ciphertext; + } + + protected decrypt(k: bytes32, n: uint32, ad: bytes, ciphertext: bytes): bytes { + const nonce = this.nonceToBytes(n); + const ctx = new AEAD(); + + ctx.init(k, nonce); + ctx.aad(ad); + ctx.decrypt(ciphertext); + + // Decryption is done on the sent reference + return ciphertext; + } + + protected decryptAndHash(ss: SymmetricState, ciphertext: bytes): bytes { + let plaintext; + if (this.hasKey(ss.cs)) { + plaintext = this.decryptWithAd(ss.cs, ss.h, ciphertext); + } else { + plaintext = ciphertext; + } + + this.mixHash(ss, ciphertext); + return plaintext; + } protected dh(privateKey: bytes32, publicKey: bytes32): bytes32 { const derived = x25519.derive(publicKey, privateKey); @@ -57,4 +125,35 @@ export class AbstractHandshake { const n = this.minNonce; return { k, n }; } + + // Symmetric state related + + protected initializeSymmetric(protocolName: string): SymmetricState { + const protocolNameBytes: bytes = Buffer.from(protocolName, 'utf-8'); + const h = this.hashProtocolName(protocolNameBytes); + + const ck = h; + const key = this.createEmptyKey(); + const cs: CipherState = this.initializeKey(key); + + return { cs, ck, h }; + } + + protected hashProtocolName(protocolName: bytes): bytes32 { + if (protocolName.length <= 32) { + const h = Buffer.alloc(32); + protocolName.copy(h); + return h; + } else { + return this.getHash(protocolName, Buffer.alloc(0)); + } + } + + protected split (ss: SymmetricState) { + const [ tempk1, tempk2 ] = getHkdf(ss.ck, Buffer.alloc(0)); + const cs1 = this.initializeKey(tempk1); + const cs2 = this.initializeKey(tempk2); + + return { cs1, cs2 }; + } } diff --git a/src/handshakes/xx.ts b/src/handshakes/xx.ts index 3273ed6..6e95ccc 100644 --- a/src/handshakes/xx.ts +++ b/src/handshakes/xx.ts @@ -10,10 +10,6 @@ import {AbstractHandshake} from "./abstract-handshake"; export class XXHandshake extends AbstractHandshake { - private createEmptyKey(): bytes32 { - return Buffer.alloc(32); - } - private initializeInitiator(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): HandshakeState { const name = "Noise_XX_25519_ChaChaPoly_SHA256"; const ss = this.initializeSymmetric(name); @@ -32,101 +28,6 @@ export class XXHandshake extends AbstractHandshake { return { ss, s, rs, psk, re }; } - private decrypt(k: bytes32, n: uint32, ad: bytes, ciphertext: bytes): bytes { - const nonce = this.nonceToBytes(n); - const ctx = new AEAD(); - - ctx.init(k, nonce); - ctx.aad(ad); - ctx.decrypt(ciphertext); - - // Decryption is done on the sent reference - return ciphertext; - } - - private isEmptyKey(k: bytes32): boolean { - const emptyKey = this.createEmptyKey(); - return emptyKey.equals(k); - } - - // Cipher state related - private hasKey(cs: CipherState): boolean { - return !this.isEmptyKey(cs.k); - } - - private setNonce(cs: CipherState, nonce: uint32): void { - cs.n = nonce; - } - - public encryptWithAd(cs: CipherState, ad: bytes, plaintext: bytes): bytes { - const e = this.encrypt(cs.k, cs.n, ad, plaintext); - this.setNonce(cs, this.incrementNonce(cs.n)); - - return e; - } - - public decryptWithAd(cs: CipherState, ad: bytes, ciphertext: bytes): bytes { - const plaintext = this.decrypt(cs.k, cs.n, ad, ciphertext); - this.setNonce(cs, this.incrementNonce(cs.n)); - - return plaintext; - } - - // Symmetric state related - - private initializeSymmetric(protocolName: string): SymmetricState { - const protocolNameBytes: bytes = Buffer.from(protocolName, 'utf-8'); - const h = this.hashProtocolName(protocolNameBytes); - - const ck = h; - const key = this.createEmptyKey(); - const cs: CipherState = this.initializeKey(key); - - return { cs, ck, h }; - } - - private hashProtocolName(protocolName: bytes): bytes32 { - if (protocolName.length <= 32) { - const h = Buffer.alloc(32); - protocolName.copy(h); - return h; - } else { - return this.getHash(protocolName, Buffer.alloc(0)); - } - } - - private encryptAndHash(ss: SymmetricState, plaintext: bytes): bytes { - let ciphertext; - if (this.hasKey(ss.cs)) { - ciphertext = this.encryptWithAd(ss.cs, ss.h, plaintext); - } else { - ciphertext = plaintext; - } - - this.mixHash(ss, ciphertext); - return ciphertext; - } - - private decryptAndHash(ss: SymmetricState, ciphertext: bytes): bytes { - let plaintext; - if (this.hasKey(ss.cs)) { - plaintext = this.decryptWithAd(ss.cs, ss.h, ciphertext); - } else { - plaintext = ciphertext; - } - - this.mixHash(ss, ciphertext); - return plaintext; - } - - private split (ss: SymmetricState) { - const [ tempk1, tempk2 ] = getHkdf(ss.ck, Buffer.alloc(0)); - const cs1 = this.initializeKey(tempk1); - const cs2 = this.initializeKey(tempk2); - - return { cs1, cs2 }; - } - private writeMessageA(hs: HandshakeState, payload: bytes): MessageBuffer { const ns = Buffer.alloc(0); hs.e = generateKeypair(); diff --git a/test/handshakes/xx.test.ts b/test/handshakes/xx.test.ts index 4bc7e1a..b6d93f4 100644 --- a/test/handshakes/xx.test.ts +++ b/test/handshakes/xx.test.ts @@ -4,7 +4,7 @@ import { Buffer } from 'buffer'; import { XXHandshake } from "../../src/handshakes/xx"; import { KeyPair } from "../../src/@types/libp2p"; import { generateEd25519Keys } from "../utils"; -import {createHandshakePayload, generateKeypair, getHandshakePayload} from "../../src/utils"; +import {createHandshakePayload, generateKeypair, getHandshakePayload, getHkdf} from "../../src/utils"; describe("Index", () => { const prologue = Buffer.from("/noise", "utf-8"); @@ -29,7 +29,7 @@ describe("Index", () => { const ck = Buffer.alloc(32); ckBytes.copy(ck); - const [k1, k2, k3] = xx.getHkdf(ck, ikm); + const [k1, k2, k3] = getHkdf(ck, ikm); expect(k1.toString('hex')).to.equal('cc5659adff12714982f806e2477a8d5ddd071def4c29bb38777b7e37046f6914'); expect(k2.toString('hex')).to.equal('a16ada915e551ab623f38be674bb4ef15d428ae9d80688899c9ef9b62ef208fa'); expect(k3.toString('hex')).to.equal('ff67bf9727e31b06efc203907e6786667d2c7a74ac412b4d31a80ba3fd766f68'); From b98c5b4513978992d7eafae33a3302a51c9aa68e Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Wed, 25 Dec 2019 18:32:19 +0100 Subject: [PATCH 05/10] Finish porting IK handshake --- src/handshakes/abstract-handshake.ts | 16 ++- src/handshakes/ik.ts | 149 ++++++++++++++++++++++++++- src/handshakes/xx.ts | 12 --- 3 files changed, 162 insertions(+), 15 deletions(-) diff --git a/src/handshakes/abstract-handshake.ts b/src/handshakes/abstract-handshake.ts index 8b99f74..c3fb26f 100644 --- a/src/handshakes/abstract-handshake.ts +++ b/src/handshakes/abstract-handshake.ts @@ -2,7 +2,7 @@ import {Buffer} from "buffer"; import { AEAD, x25519, HKDF, SHA256 } from 'bcrypto'; import {bytes, bytes32, uint32} from "../@types/basic"; -import {CipherState, SymmetricState} from "../@types/handshake"; +import {CipherState, MessageBuffer, SymmetricState} from "../@types/handshake"; import {getHkdf} from "../utils"; export class AbstractHandshake { @@ -149,11 +149,23 @@ export class AbstractHandshake { } } - protected split (ss: SymmetricState) { + protected split(ss: SymmetricState) { const [ tempk1, tempk2 ] = getHkdf(ss.ck, Buffer.alloc(0)); const cs1 = this.initializeKey(tempk1); const cs2 = this.initializeKey(tempk2); return { cs1, cs2 }; } + + protected writeMessageRegular(cs: CipherState, payload: bytes): MessageBuffer { + const ciphertext = this.encryptWithAd(cs, Buffer.alloc(0), payload); + const ne = this.createEmptyKey(); + const ns = Buffer.alloc(0); + + return { ne, ns, ciphertext }; + } + + protected readMessageRegular(cs: CipherState, message: MessageBuffer): bytes { + return this.decryptWithAd(cs, Buffer.alloc(0), message.ciphertext); + } } diff --git a/src/handshakes/ik.ts b/src/handshakes/ik.ts index bf46def..2f53bf1 100644 --- a/src/handshakes/ik.ts +++ b/src/handshakes/ik.ts @@ -1,12 +1,94 @@ import {Buffer} from "buffer"; +import {x25519} from "bcrypto"; -import {CipherState, HandshakeState, MessageBuffer, SymmetricState} from "../@types/handshake"; +import {CipherState, HandshakeState, MessageBuffer, NoiseSession, SymmetricState} from "../@types/handshake"; import {bytes, bytes32} from "../@types/basic"; import {generateKeypair, getHkdf} from "../utils"; import {AbstractHandshake} from "./abstract-handshake"; +import {KeyPair} from "../@types/libp2p"; +import {BN} from "bn.js"; export class IKHandshake extends AbstractHandshake { + public initSession(initiator: boolean, prologue: bytes32, s: KeyPair, rs: bytes32): NoiseSession { + const psk = this.createEmptyKey(); + + let hs; + if (initiator) { + hs = this.initializeInitiator(prologue, s, rs, psk); + } else { + hs = this.initializeResponder(prologue, s, rs, psk); + } + + return { + hs, + i: initiator, + mc: new BN(0), + }; + } + + public sendMessage(session: NoiseSession, message: bytes): MessageBuffer { + let messageBuffer: MessageBuffer; + if (session.mc.eqn(0)) { + messageBuffer = this.writeMessageA(session.hs, message); + } else if (session.mc.eqn(1)) { + const { messageBuffer: mb, h, cs1, cs2 } = this.writeMessageB(session.hs, message); + messageBuffer = mb; + session.h = h; + session.cs1 = cs1; + session.cs2 = cs2; + } else if (session.mc.gtn(1)) { + if (session.i) { + if (!session.cs1) { + throw new Error("CS1 (cipher state) is not defined") + } + + messageBuffer = this.writeMessageRegular(session.cs1, message); + } else { + if (!session.cs2) { + throw new Error("CS2 (cipher state) is not defined") + } + + messageBuffer = this.writeMessageRegular(session.cs2, message); + } + } else { + throw new Error("Session invalid.") + } + + session.mc = session.mc.add(new BN(1)); + return messageBuffer; + } + + public recvMessage(session: NoiseSession, message: MessageBuffer): bytes { + let plaintext: bytes; + if (session.mc.eqn(0)) { + plaintext = this.readMessageA(session.hs, message); + } else if (session.mc.eqn(1)) { + const { plaintext: pt, h, cs1, cs2 } = this.readMessageB(session.hs, message); + plaintext = pt; + session.h = h; + session.cs1 = cs1; + session.cs2 = cs2; + } else if (session.mc.gtn(1)) { + if (session.i) { + if (!session.cs2) { + throw new Error("CS1 (cipher state) is not defined") + } + plaintext = this.readMessageRegular(session.cs2, message); + } else { + if (!session.cs1) { + throw new Error("CS1 (cipher state) is not defined") + } + plaintext = this.readMessageRegular(session.cs1, message); + } + } else { + throw new Error("Session invalid."); + } + + session.mc = session.mc.add(new BN(1)); + return plaintext; + } + private writeMessageA(hs: HandshakeState, payload: bytes): MessageBuffer { hs.e = generateKeypair(); const ne = hs.e.publicKey; @@ -21,5 +103,70 @@ export class IKHandshake extends AbstractHandshake { return { ne, ns, ciphertext }; } + private writeMessageB(hs: HandshakeState, payload: bytes) { + hs.e = generateKeypair(); + const ne = hs.e.publicKey; + this.mixHash(hs.ss, ne); + this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.re)); + this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs)); + const ciphertext = this.encryptAndHash(hs.ss, payload); + const ns = this.createEmptyKey(); + const messageBuffer: MessageBuffer = {ne, ns, ciphertext}; + const { cs1, cs2 } = this.split(hs.ss); + + return { messageBuffer, cs1, cs2, h: hs.ss.h } + } + + private readMessageA(hs: HandshakeState, message: MessageBuffer): bytes { + if (x25519.publicKeyVerify(message.ne)) { + hs.re = message.ne; + } + + this.mixHash(hs.ss, hs.re); + this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.re)); + const ns = this.decryptAndHash(hs.ss, message.ns); + if (ns.length === 32 && x25519.publicKeyVerify(message.ns)) { + hs.rs = ns; + } + this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.rs)); + return this.decryptAndHash(hs.ss, message.ciphertext); + } + + private readMessageB(hs: HandshakeState, message: MessageBuffer) { + if (x25519.publicKeyVerify(message.ne)) { + hs.re = message.ne; + } + + this.mixHash(hs.ss, hs.re); + if (!hs.e) { + throw new Error("Handshake state should contain ephemeral key by now."); + } + this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.re)); + this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.re)); + const plaintext = this.decryptAndHash(hs.ss, message.ciphertext); + const { cs1, cs2 } = this.split(hs.ss); + + return { h: hs.ss.h, plaintext, cs1, cs2 }; + } + + private initializeInitiator(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): HandshakeState { + const name = "Noise_IK_25519_ChaChaPoly_SHA256"; + const ss = this.initializeSymmetric(name); + this.mixHash(ss, prologue); + this.mixHash(ss, rs); + const re = Buffer.alloc(32); + + return { ss, s, rs, re, psk }; + } + + private initializeResponder(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): HandshakeState { + const name = "Noise_IK_25519_ChaChaPoly_SHA256"; + const ss = this.initializeSymmetric(name); + this.mixHash(ss, prologue); + this.mixHash(ss, s.publicKey); + const re = Buffer.alloc(32); + + return { ss, s, rs, re, psk }; + } } diff --git a/src/handshakes/xx.ts b/src/handshakes/xx.ts index 6e95ccc..3bee635 100644 --- a/src/handshakes/xx.ts +++ b/src/handshakes/xx.ts @@ -67,14 +67,6 @@ export class XXHandshake extends AbstractHandshake { return { h: hs.ss.h, messageBuffer, cs1, cs2 }; } - private writeMessageRegular(cs: CipherState, payload: bytes): MessageBuffer { - const ciphertext = this.encryptWithAd(cs, Buffer.alloc(0), payload); - const ne = this.createEmptyKey(); - const ns = Buffer.alloc(0); - - return { ne, ns, ciphertext }; - } - private readMessageA(hs: HandshakeState, message: MessageBuffer): bytes { if (x25519.publicKeyVerify(message.ne)) { hs.re = message.ne; @@ -119,10 +111,6 @@ export class XXHandshake extends AbstractHandshake { return { h: hs.ss.h, plaintext, cs1, cs2 }; } - private readMessageRegular(cs: CipherState, message: MessageBuffer): bytes { - return this.decryptWithAd(cs, Buffer.alloc(0), message.ciphertext); - } - public initSession(initiator: boolean, prologue: bytes32, s: KeyPair): NoiseSession { const psk = this.createEmptyKey(); const rs = Buffer.alloc(32); // no static key yet From 10a41c5ea60947a3dbcb9b4427cc4558d58662d1 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Wed, 25 Dec 2019 22:02:07 +0100 Subject: [PATCH 06/10] Write IK test --- test/handshakes/ik.test.ts | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/test/handshakes/ik.test.ts b/test/handshakes/ik.test.ts index e69de29..c357be2 100644 --- a/test/handshakes/ik.test.ts +++ b/test/handshakes/ik.test.ts @@ -0,0 +1,63 @@ +import {Buffer} from "buffer"; +import {IKHandshake} from "../../src/handshakes/ik"; +import {KeyPair} from "../../src/@types/libp2p"; +import {createHandshakePayload, generateKeypair, getHandshakePayload} from "../../src/utils"; +import {assert, expect} from "chai"; +import {generateEd25519Keys} from "../utils"; + +describe("Index", () => { + const prologue = Buffer.from("/noise", "utf-8"); + + it("Test complete IK handshake", async () => { + try { + const ik = new IKHandshake(); + + // Generate static noise keys + const kpInitiator: KeyPair = await generateKeypair(); + const kpResponder: KeyPair = await generateKeypair(); + + // Generate libp2p keys + const libp2pInitKeys = await generateEd25519Keys(); + const libp2pRespKeys = await generateEd25519Keys(); + + // Create sessions + const initiatorSession = await ik.initSession(true, prologue, kpInitiator, kpResponder.publicKey); + const responderSession = await ik.initSession(false, prologue, kpResponder, Buffer.alloc(32)); + + /* Stage 0 */ + + // initiator creates payload + const initSignedPayload = await libp2pInitKeys.sign(getHandshakePayload(kpInitiator.publicKey)); + const libp2pInitPrivKey = libp2pInitKeys.marshal().slice(0, 32); + const libp2pInitPubKey = libp2pInitKeys.marshal().slice(32, 64); + const payloadInitEnc = await createHandshakePayload(libp2pInitPubKey, libp2pInitPrivKey, initSignedPayload); + + // initiator sends message + const message = Buffer.concat([Buffer.alloc(0), payloadInitEnc]); + const messageBuffer = ik.sendMessage(initiatorSession, message); + + expect(messageBuffer.ne.length).not.equal(0); + + // responder receives message + const plaintext = ik.recvMessage(responderSession, messageBuffer); + console.log("Stage 0 responder payload: ", plaintext); + + /* Stage 1 */ + + // responder creates payload + const libp2pRespPrivKey = libp2pRespKeys.marshal().slice(0, 32); + const libp2pRespPubKey = libp2pRespKeys.marshal().slice(32, 64); + const respSignedPayload = await libp2pRespKeys.sign(getHandshakePayload(kpResponder.publicKey)); + const payloadRespEnc = await createHandshakePayload(libp2pRespPubKey, libp2pRespPrivKey, respSignedPayload); + + const message1 = Buffer.concat([message, payloadRespEnc]); + const messageBuffer2 = ik.sendMessage(responderSession, message1); + + // initator receives message + const plaintext2 = ik.recvMessage(initiatorSession, messageBuffer2); + + } catch (e) { + assert(false, e.message); + } + }); +}); From 741f6c9788540dc83e576a1da0637f09d8c018d4 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Wed, 25 Dec 2019 22:08:18 +0100 Subject: [PATCH 07/10] Fix test --- src/handshakes/ik.ts | 2 +- test/handshakes/ik.test.ts | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/handshakes/ik.ts b/src/handshakes/ik.ts index 2f53bf1..03b2c52 100644 --- a/src/handshakes/ik.ts +++ b/src/handshakes/ik.ts @@ -97,7 +97,7 @@ export class IKHandshake extends AbstractHandshake { const spk = Buffer.from(hs.s.publicKey); const ns = this.encryptAndHash(hs.ss, spk); - this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.re)); + this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.rs)); const ciphertext = this.encryptAndHash(hs.ss, payload); return { ne, ns, ciphertext }; diff --git a/test/handshakes/ik.test.ts b/test/handshakes/ik.test.ts index c357be2..6a4bf8e 100644 --- a/test/handshakes/ik.test.ts +++ b/test/handshakes/ik.test.ts @@ -10,7 +10,8 @@ describe("Index", () => { it("Test complete IK handshake", async () => { try { - const ik = new IKHandshake(); + const ikI = new IKHandshake(); + const ikR = new IKHandshake(); // Generate static noise keys const kpInitiator: KeyPair = await generateKeypair(); @@ -21,8 +22,8 @@ describe("Index", () => { const libp2pRespKeys = await generateEd25519Keys(); // Create sessions - const initiatorSession = await ik.initSession(true, prologue, kpInitiator, kpResponder.publicKey); - const responderSession = await ik.initSession(false, prologue, kpResponder, Buffer.alloc(32)); + const initiatorSession = await ikI.initSession(true, prologue, kpInitiator, kpResponder.publicKey); + const responderSession = await ikR.initSession(false, prologue, kpResponder, Buffer.alloc(32)); /* Stage 0 */ @@ -34,13 +35,12 @@ describe("Index", () => { // initiator sends message const message = Buffer.concat([Buffer.alloc(0), payloadInitEnc]); - const messageBuffer = ik.sendMessage(initiatorSession, message); + const messageBuffer = ikI.sendMessage(initiatorSession, message); expect(messageBuffer.ne.length).not.equal(0); // responder receives message - const plaintext = ik.recvMessage(responderSession, messageBuffer); - console.log("Stage 0 responder payload: ", plaintext); + const plaintext = ikR.recvMessage(responderSession, messageBuffer); /* Stage 1 */ @@ -51,12 +51,16 @@ describe("Index", () => { const payloadRespEnc = await createHandshakePayload(libp2pRespPubKey, libp2pRespPrivKey, respSignedPayload); const message1 = Buffer.concat([message, payloadRespEnc]); - const messageBuffer2 = ik.sendMessage(responderSession, message1); + const messageBuffer2 = ikR.sendMessage(responderSession, message1); - // initator receives message - const plaintext2 = ik.recvMessage(initiatorSession, messageBuffer2); + // initiator receives message + const plaintext2 = ikI.recvMessage(initiatorSession, messageBuffer2); + + assert(initiatorSession.cs1.k.equals(responderSession.cs1.k)); + assert(initiatorSession.cs2.k.equals(responderSession.cs2.k)); } catch (e) { + console.log(e); assert(false, e.message); } }); From 3f8feee67cade62eb7c38e2535cdcf32ff43ee77 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Wed, 25 Dec 2019 22:11:02 +0100 Subject: [PATCH 08/10] fixo --- src/handshakes/abstract-handshake.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handshakes/abstract-handshake.ts b/src/handshakes/abstract-handshake.ts index c3fb26f..f775fe7 100644 --- a/src/handshakes/abstract-handshake.ts +++ b/src/handshakes/abstract-handshake.ts @@ -5,7 +5,7 @@ import {bytes, bytes32, uint32} from "../@types/basic"; import {CipherState, MessageBuffer, SymmetricState} from "../@types/handshake"; import {getHkdf} from "../utils"; -export class AbstractHandshake { +export abstract class AbstractHandshake { protected minNonce = 0; public encryptWithAd(cs: CipherState, ad: bytes, plaintext: bytes): bytes { From 7bad317d0ed1f8b644e7f882e5b9283bb36c529f Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Sun, 29 Dec 2019 18:23:43 +0100 Subject: [PATCH 09/10] Address PR comments --- src/encoder.ts | 8 ++++---- src/handshakes/abstract-handshake.ts | 8 ++++---- src/handshakes/ik.ts | 14 +++++++------- src/handshakes/xx.ts | 16 ++++++++-------- src/noise.ts | 6 +++--- src/utils.ts | 4 ++++ 6 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/encoder.ts b/src/encoder.ts index 8837d4f..e4e2c30 100644 --- a/src/encoder.ts +++ b/src/encoder.ts @@ -2,17 +2,17 @@ import {Buffer} from "buffer"; import {bytes} from "./@types/basic"; import {MessageBuffer} from "./@types/handshake"; -export const int16BEEncode = (value, target, offset) => { +export const uint16BEEncode = (value, target, offset) => { target = target || Buffer.allocUnsafe(2); return target.writeUInt16BE(value, offset); }; -int16BEEncode.bytes = 2; +uint16BEEncode.bytes = 2; -export const int16BEDecode = data => { +export const uint16BEDecode = data => { if (data.length < 2) throw RangeError('Could not decode int16BE'); return data.readUInt16BE(0); }; -int16BEDecode.bytes = 2; +uint16BEDecode.bytes = 2; export function encodeMessageBuffer(message: MessageBuffer): bytes { return Buffer.concat([message.ne, message.ns, message.ciphertext]); diff --git a/src/handshakes/abstract-handshake.ts b/src/handshakes/abstract-handshake.ts index f775fe7..24d1d6c 100644 --- a/src/handshakes/abstract-handshake.ts +++ b/src/handshakes/abstract-handshake.ts @@ -1,13 +1,13 @@ import {Buffer} from "buffer"; -import { AEAD, x25519, HKDF, SHA256 } from 'bcrypto'; +import { AEAD, x25519, SHA256 } from 'bcrypto'; import {bytes, bytes32, uint32} from "../@types/basic"; import {CipherState, MessageBuffer, SymmetricState} from "../@types/handshake"; import {getHkdf} from "../utils"; -export abstract class AbstractHandshake { - protected minNonce = 0; +const minNonce = 0; +export abstract class AbstractHandshake { public encryptWithAd(cs: CipherState, ad: bytes, plaintext: bytes): bytes { const e = this.encrypt(cs.k, cs.n, ad, plaintext); this.setNonce(cs, this.incrementNonce(cs.n)); @@ -122,7 +122,7 @@ export abstract class AbstractHandshake { } protected initializeKey(k: bytes32): CipherState { - const n = this.minNonce; + const n = minNonce; return { k, n }; } diff --git a/src/handshakes/ik.ts b/src/handshakes/ik.ts index 03b2c52..f7c04ff 100644 --- a/src/handshakes/ik.ts +++ b/src/handshakes/ik.ts @@ -1,12 +1,11 @@ import {Buffer} from "buffer"; -import {x25519} from "bcrypto"; +import {BN} from "bn.js"; -import {CipherState, HandshakeState, MessageBuffer, NoiseSession, SymmetricState} from "../@types/handshake"; +import {HandshakeState, MessageBuffer, NoiseSession} from "../@types/handshake"; import {bytes, bytes32} from "../@types/basic"; -import {generateKeypair, getHkdf} from "../utils"; +import {generateKeypair, getHkdf, isValidPublicKey} from "../utils"; import {AbstractHandshake} from "./abstract-handshake"; import {KeyPair} from "../@types/libp2p"; -import {BN} from "bn.js"; export class IKHandshake extends AbstractHandshake { @@ -69,6 +68,7 @@ export class IKHandshake extends AbstractHandshake { session.h = h; session.cs1 = cs1; session.cs2 = cs2; + delete session.hs; } else if (session.mc.gtn(1)) { if (session.i) { if (!session.cs2) { @@ -119,14 +119,14 @@ export class IKHandshake extends AbstractHandshake { } private readMessageA(hs: HandshakeState, message: MessageBuffer): bytes { - if (x25519.publicKeyVerify(message.ne)) { + if (isValidPublicKey(message.ne)) { hs.re = message.ne; } this.mixHash(hs.ss, hs.re); this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.re)); const ns = this.decryptAndHash(hs.ss, message.ns); - if (ns.length === 32 && x25519.publicKeyVerify(message.ns)) { + if (ns.length === 32 && isValidPublicKey(message.ns)) { hs.rs = ns; } this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.rs)); @@ -134,7 +134,7 @@ export class IKHandshake extends AbstractHandshake { } private readMessageB(hs: HandshakeState, message: MessageBuffer) { - if (x25519.publicKeyVerify(message.ne)) { + if (isValidPublicKey(message.ne)) { hs.re = message.ne; } diff --git a/src/handshakes/xx.ts b/src/handshakes/xx.ts index 3bee635..3b8b0a9 100644 --- a/src/handshakes/xx.ts +++ b/src/handshakes/xx.ts @@ -1,11 +1,10 @@ import { Buffer } from 'buffer'; -import { AEAD, x25519, HKDF, SHA256 } from 'bcrypto'; import { BN } from 'bn.js'; -import { bytes32, uint32, uint64, bytes } from '../@types/basic' +import { bytes32, bytes } from '../@types/basic' import { KeyPair } from '../@types/libp2p' -import {generateKeypair, getHkdf} from '../utils'; -import { CipherState, HandshakeState, Hkdf, MessageBuffer, NoiseSession, SymmetricState } from "../@types/handshake"; +import {generateKeypair, getHkdf, isValidPublicKey} from '../utils'; +import { HandshakeState, MessageBuffer, NoiseSession } from "../@types/handshake"; import {AbstractHandshake} from "./abstract-handshake"; @@ -68,7 +67,7 @@ export class XXHandshake extends AbstractHandshake { } private readMessageA(hs: HandshakeState, message: MessageBuffer): bytes { - if (x25519.publicKeyVerify(message.ne)) { + if (isValidPublicKey(message.ne)) { hs.re = message.ne; } @@ -77,7 +76,7 @@ export class XXHandshake extends AbstractHandshake { } private readMessageB(hs: HandshakeState, message: MessageBuffer): bytes { - if (x25519.publicKeyVerify(message.ne)) { + if (isValidPublicKey(message.ne)) { hs.re = message.ne; } @@ -87,7 +86,7 @@ export class XXHandshake extends AbstractHandshake { } this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.re)); const ns = this.decryptAndHash(hs.ss, message.ns); - if (ns.length === 32 && x25519.publicKeyVerify(message.ns)) { + if (ns.length === 32 && isValidPublicKey(message.ns)) { hs.rs = ns; } this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs)); @@ -96,7 +95,7 @@ export class XXHandshake extends AbstractHandshake { private readMessageC(hs: HandshakeState, message: MessageBuffer) { const ns = this.decryptAndHash(hs.ss, message.ns); - if (ns.length === 32 && x25519.publicKeyVerify(message.ns)) { + if (ns.length === 32 && isValidPublicKey(message.ns)) { hs.rs = ns; } @@ -141,6 +140,7 @@ export class XXHandshake extends AbstractHandshake { session.h = h; session.cs1 = cs1; session.cs2 = cs2; + delete session.hs; } else if (session.mc.gtn(2)) { if (session.i) { if (!session.cs1) { diff --git a/src/noise.ts b/src/noise.ts index 83b313e..068ade3 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -8,7 +8,7 @@ import lp from 'it-length-prefixed'; import { Handshake } from "./handshake"; import { generateKeypair } from "./utils"; -import { int16BEDecode, int16BEEncode } from "./encoder"; +import { uint16BEDecode, uint16BEEncode } from "./encoder"; import { decryptStream, encryptStream } from "./crypto"; import { bytes } from "./@types/basic"; import { NoiseConnection, PeerId, KeyPair, SecureOutbound } from "./@types/libp2p"; @@ -108,9 +108,9 @@ export class Noise implements NoiseConnection { secure, // write to wrapper ensureBuffer, // ensure any type of data is converted to buffer encryptStream(handshake), // data is encrypted - lp.encode({ lengthEncoder: int16BEEncode }), // prefix with message length + lp.encode({ lengthEncoder: uint16BEEncode }), // prefix with message length network, // send to the remote peer - lp.decode({ lengthDecoder: int16BEDecode }), // read message length prefix + lp.decode({ lengthDecoder: uint16BEDecode }), // read message length prefix ensureBuffer, // ensure any type of data is converted to buffer decryptStream(handshake), // decrypt the incoming data secure // pipe to the wrapper diff --git a/src/utils.ts b/src/utils.ts index e749040..005f826 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -101,3 +101,7 @@ export function getHkdf(ck: bytes32, ikm: bytes): Hkdf { return [ k1, k2, k3 ]; } + +export function isValidPublicKey(pk: bytes): boolean { + return x25519.publicKeyVerify(pk); +} From 4dc1a2ab95d39f556c6f3918e6bc70f5a9f18985 Mon Sep 17 00:00:00 2001 From: Belma Gutlic Date: Mon, 30 Dec 2019 09:59:59 +0100 Subject: [PATCH 10/10] Address PR comment, rename constant --- src/handshakes/abstract-handshake.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handshakes/abstract-handshake.ts b/src/handshakes/abstract-handshake.ts index 24d1d6c..486dd96 100644 --- a/src/handshakes/abstract-handshake.ts +++ b/src/handshakes/abstract-handshake.ts @@ -5,7 +5,7 @@ import {bytes, bytes32, uint32} from "../@types/basic"; import {CipherState, MessageBuffer, SymmetricState} from "../@types/handshake"; import {getHkdf} from "../utils"; -const minNonce = 0; +export const MIN_NONCE = 0; export abstract class AbstractHandshake { public encryptWithAd(cs: CipherState, ad: bytes, plaintext: bytes): bytes { @@ -122,7 +122,7 @@ export abstract class AbstractHandshake { } protected initializeKey(k: bytes32): CipherState { - const n = minNonce; + const n = MIN_NONCE; return { k, n }; }