Merge pull request #5 from NodeFactoryIo/morrigan/verify-signature

Authenticate keys - verify signature
This commit is contained in:
Marin Petrunić 2019-12-03 18:29:14 +01:00 committed by GitHub
commit 5167df18a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 363 additions and 192 deletions

View File

@ -3,6 +3,10 @@
[![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](https://libp2p.io/) [![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](https://libp2p.io/)
![](https://img.shields.io/github/issues-raw/nodefactoryio/js-libp2p-noise) ![](https://img.shields.io/github/issues-raw/nodefactoryio/js-libp2p-noise)
![](https://img.shields.io/github/license/nodefactoryio/js-libp2p-noise) ![](https://img.shields.io/github/license/nodefactoryio/js-libp2p-noise)
[![Build Status](https://travis-ci.com/NodeFactoryIo/js-libp2p-noise.svg?branch=master)](https://travis-ci.com/NodeFactoryIo/js-libp2p-noise)
![](https://img.shields.io/badge/yarn-%3E%3D1.17.0-orange.svg?style=flat-square)
![](https://img.shields.io/badge/Node.js-%3E%3D12.4.0-orange.svg?style=flat-square)
[![Discourse posts](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io)
> Noise libp2p handshake for js-libp2p > Noise libp2p handshake for js-libp2p
@ -10,8 +14,39 @@ This repository contains TypeScript implementation of noise protocol, an encrypt
## Usage ## Usage
TBD When published, package should be imported as: `import { Noise } from 'libp2p-noise'`.
Example of instantiating noise and passing it to the libp2p config:
```
const NOISE = new Noise(privateKey);
const libp2p = new Libp2p({
modules: {
connEncryption: [NOISE],
},
});
```
Where parameters for Noise constructor are:
- *private key* - required parameter (32 bytes libp2p peer private key)
- *static Noise key* - (optional) existing private Noise static key
- *early data* - (optional) an early data payload to be sent in handshake messages
## API ## API
TBD This module exposes a crypto interface, as defined in the repository [js-interfaces](https://github.com/libp2p/js-interfaces).
[» API Docs](https://github.com/libp2p/js-interfaces/tree/master/src/crypto#api)
## Contribute
Feel free to join in. All welcome. Open an issue!
[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md)
## License
[MIT](LICENSE)

View File

@ -5,6 +5,11 @@
"repository": "git@github.com:NodeFactoryIo/js-libp2p-noise.git", "repository": "git@github.com:NodeFactoryIo/js-libp2p-noise.git",
"author": "NodeFactory <info@nodefactory.io>", "author": "NodeFactory <info@nodefactory.io>",
"license": "MIT", "license": "MIT",
"keywords": [
"libp2p",
"noise",
"crypto"
],
"scripts": { "scripts": {
"prebuild": "rm -rf lib", "prebuild": "rm -rf lib",
"build": "babel src -x .ts -d lib --source-maps", "build": "babel src -x .ts -d lib --source-maps",
@ -13,27 +18,6 @@
"pretest": "yarn check-types", "pretest": "yarn check-types",
"test": "DEBUG=libp2p:noise mocha -r ./babel-register.js \"test/**/*.test.ts\"" "test": "DEBUG=libp2p:noise mocha -r ./babel-register.js \"test/**/*.test.ts\""
}, },
"devDependencies": {
"@babel/cli": "^7.6.4",
"@babel/core": "^7.6.4",
"@babel/plugin-proposal-async-generator-functions": "^7.7.0",
"@babel/plugin-proposal-object-rest-spread": "^7.6.2",
"@babel/preset-env": "^7.6.3",
"@babel/preset-typescript": "^7.6.0",
"@babel/register": "^7.6.2",
"@babel/runtime": "^7.6.3",
"@types/chai": "^4.2.4",
"@types/mocha": "^5.2.7",
"@typescript-eslint/eslint-plugin": "^2.6.0",
"@typescript-eslint/parser": "^2.6.0",
"bn.js-typings": "^1.0.1",
"chai": "^4.2.0",
"eslint": "^6.6.0",
"libp2p-crypto": "^0.17.1",
"mocha": "^6.2.2",
"peer-id": "^0.13.5",
"typescript": "^3.6.4"
},
"babel": { "babel": {
"presets": [ "presets": [
[ [
@ -51,6 +35,25 @@
"@babel/plugin-proposal-async-generator-functions" "@babel/plugin-proposal-async-generator-functions"
] ]
}, },
"devDependencies": {
"@babel/cli": "^7.6.4",
"@babel/core": "^7.6.4",
"@babel/plugin-proposal-async-generator-functions": "^7.7.0",
"@babel/plugin-proposal-object-rest-spread": "^7.6.2",
"@babel/preset-env": "^7.6.3",
"@babel/preset-typescript": "^7.6.0",
"@babel/register": "^7.6.2",
"@babel/runtime": "^7.6.3",
"@types/chai": "^4.2.4",
"@types/mocha": "^5.2.7",
"@typescript-eslint/eslint-plugin": "^2.6.0",
"@typescript-eslint/parser": "^2.6.0",
"bn.js-typings": "^1.0.1",
"chai": "^4.2.0",
"eslint": "^6.6.0",
"mocha": "^6.2.2",
"typescript": "^3.6.4"
},
"dependencies": { "dependencies": {
"bcrypto": "^4.2.3", "bcrypto": "^4.2.3",
"bn.js": "^5.0.0", "bn.js": "^5.0.0",
@ -61,6 +64,8 @@
"it-pair": "^1.0.0", "it-pair": "^1.0.0",
"it-pb-rpc": "^0.1.3", "it-pb-rpc": "^0.1.3",
"it-pipe": "^1.1.0", "it-pipe": "^1.1.0",
"libp2p-crypto": "^0.17.1",
"peer-id": "^0.13.5",
"protobufjs": "~6.8.8" "protobufjs": "~6.8.8"
} }
} }

View File

@ -14,6 +14,8 @@ export type PeerId = {
pubKey: { pubKey: {
marshal(): bytes; marshal(): bytes;
}; };
marshalPubKey(): bytes;
marshalPrivKey(): bytes;
}; };
export interface NoiseConnection { export interface NoiseConnection {

View File

@ -1,4 +1,3 @@
import { Duplex } from "it-pair";
import { Handshake } from "./handshake"; import { Handshake } from "./handshake";
import { Buffer } from "buffer"; import { Buffer } from "buffer";

27
src/encoder.ts Normal file
View File

@ -0,0 +1,27 @@
import {Buffer} from "buffer";
import {bytes} from "./@types/basic";
import {MessageBuffer} from "./xx";
export const int16BEEncode = (value, target, offset) => {
target = target || Buffer.allocUnsafe(2);
return target.writeInt16BE(value, offset);
};
int16BEEncode.bytes = 2;
export const int16BEDecode = data => {
if (data.length < 2) throw RangeError('Could not decode int16BE');
return data.readInt16BE(0);
};
int16BEDecode.bytes = 2;
export function encodeMessageBuffer(message: MessageBuffer): bytes {
return Buffer.concat([message.ne, message.ns, message.ciphertext]);
}
export function decodeMessageBuffer(message: bytes): MessageBuffer {
return {
ne: message.slice(0, 32),
ns: message.slice(32, 64),
ciphertext: message.slice(64, message.length),
}
}

View File

@ -2,15 +2,16 @@ import { Buffer } from "buffer";
import { bytes, bytes32 } from "./@types/basic"; import { bytes, bytes32 } from "./@types/basic";
import { NoiseSession, XXHandshake } from "./xx"; import { NoiseSession, XXHandshake } from "./xx";
import { KeyPair } from "./@types/libp2p"; import { KeyPair, PeerId } from "./@types/libp2p";
import { import {
createHandshakePayload, createHandshakePayload,
decodeMessageBuffer,
encodeMessageBuffer,
getHandshakePayload, getHandshakePayload,
logger, signEarlyDataPayload, signEarlyDataPayload,
signPayload, signPayload,
verifySignedPayload,
} from "./utils"; } from "./utils";
import { logger } from "./logger";
import { decodeMessageBuffer, encodeMessageBuffer } from "./encoder";
import { WrappedConnection } from "./noise"; import { WrappedConnection } from "./noise";
export class Handshake { export class Handshake {
@ -22,6 +23,7 @@ export class Handshake {
private prologue: bytes32; private prologue: bytes32;
private staticKeys: KeyPair; private staticKeys: KeyPair;
private connection: WrappedConnection; private connection: WrappedConnection;
private remotePeer: PeerId;
private xx: XXHandshake; private xx: XXHandshake;
constructor( constructor(
@ -31,6 +33,7 @@ export class Handshake {
prologue: bytes32, prologue: bytes32,
staticKeys: KeyPair, staticKeys: KeyPair,
connection: WrappedConnection, connection: WrappedConnection,
remotePeer: PeerId,
handshake?: XXHandshake, handshake?: XXHandshake,
) { ) {
this.isInitiator = isInitiator; this.isInitiator = isInitiator;
@ -39,32 +42,23 @@ export class Handshake {
this.prologue = prologue; this.prologue = prologue;
this.staticKeys = staticKeys; this.staticKeys = staticKeys;
this.connection = connection; this.connection = connection;
this.remotePeer = remotePeer;
this.xx = handshake || new XXHandshake(); this.xx = handshake || new XXHandshake();
this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeys); this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeys);
} }
// stage 0 // stage 0
async propose(earlyData?: bytes): Promise<void> { async propose(): Promise<void> {
if (this.isInitiator) { if (this.isInitiator) {
logger("Stage 0 - Initiator starting to send first message."); logger("Stage 0 - Initiator starting to send first message.");
const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeys.publicKey)); const messageBuffer = this.xx.sendMessage(this.session, Buffer.alloc(0));
const signedEarlyDataPayload = signEarlyDataPayload(this.libp2pPrivateKey, earlyData || Buffer.alloc(0));
const handshakePayload = await createHandshakePayload(
this.libp2pPublicKey,
this.libp2pPrivateKey,
signedPayload,
signedEarlyDataPayload
);
const messageBuffer = await this.xx.sendMessage(this.session, handshakePayload);
this.connection.writeLP(encodeMessageBuffer(messageBuffer)); this.connection.writeLP(encodeMessageBuffer(messageBuffer));
logger("Stage 0 - Initiator finished sending first message.");
logger("Stage 0 - Initiator finished proposing, sent signed NoiseHandshake payload and static public key.");
} else { } else {
logger("Stage 0 - Responder waiting to receive first message..."); logger("Stage 0 - Responder waiting to receive first message...");
const receivedMessageBuffer = decodeMessageBuffer((await this.connection.readLP()).slice()); const receivedMessageBuffer = decodeMessageBuffer((await this.connection.readLP()).slice());
const plaintext = await this.xx.recvMessage(this.session, receivedMessageBuffer); this.xx.recvMessage(this.session, receivedMessageBuffer);
// TODO: Verify payload
logger("Stage 0 - Responder received first message."); logger("Stage 0 - Responder received first message.");
} }
} }
@ -74,36 +68,59 @@ export class Handshake {
if (this.isInitiator) { if (this.isInitiator) {
logger('Stage 1 - Initiator waiting to receive first message from responder...'); logger('Stage 1 - Initiator waiting to receive first message from responder...');
const receivedMessageBuffer = decodeMessageBuffer((await this.connection.readLP()).slice()); const receivedMessageBuffer = decodeMessageBuffer((await this.connection.readLP()).slice());
const plaintext = await this.xx.recvMessage(this.session, receivedMessageBuffer); const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer);
// TODO: Verify payload
logger('Stage 1 - Initiator received the message. Got remote\'s static key.'); logger('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 { } else {
logger('Stage 1 - Responder sending out first message with signed payload and static key.'); 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.staticKeys.publicKey));
const signedEarlyDataPayload = signEarlyDataPayload(this.libp2pPrivateKey, Buffer.alloc(0));
const handshakePayload = await createHandshakePayload( const handshakePayload = await createHandshakePayload(
this.libp2pPublicKey, this.libp2pPublicKey,
this.libp2pPrivateKey, this.libp2pPrivateKey,
signedPayload, signedPayload,
signedEarlyDataPayload,
); );
const messageBuffer = await this.xx.sendMessage(this.session, handshakePayload); const messageBuffer = this.xx.sendMessage(this.session, handshakePayload);
this.connection.writeLP(encodeMessageBuffer(messageBuffer)); this.connection.writeLP(encodeMessageBuffer(messageBuffer));
logger('Stage 1 - Responder sent the second handshake message.') logger('Stage 1 - Responder sent the second handshake message with signed payload.')
} }
} }
// stage 2 // stage 2
async finish(): Promise<void> { async finish(earlyData?: bytes): Promise<void> {
if (this.isInitiator) { if (this.isInitiator) {
logger('Stage 2 - Initiator sending third handshake message.'); logger('Stage 2 - Initiator sending third handshake message.');
const messageBuffer = await this.xx.sendMessage(this.session, Buffer.alloc(0)); const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeys.publicKey));
const signedEarlyDataPayload = signEarlyDataPayload(this.libp2pPrivateKey, earlyData || 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)); this.connection.writeLP(encodeMessageBuffer(messageBuffer));
logger('Stage 2 - Initiator sent message.'); logger('Stage 2 - Initiator sent message with signed payload.');
} else { } else {
logger('Stage 2 - Responder waiting for third handshake message...'); logger('Stage 2 - Responder waiting for third handshake message...');
const receivedMessageBuffer = (await this.connection.readLP()).slice(); const receivedMessageBuffer = decodeMessageBuffer((await this.connection.readLP()).slice());
const plaintext = await this.xx.recvMessage(this.session, decodeMessageBuffer(receivedMessageBuffer)); const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer);
logger('Stage 2 - Responder received the message, finished handshake. Got remote\'s static key.'); logger('Stage 2 - Responder received the message, finished handshake. Got remote\'s static key.');
try {
await verifySignedPayload(receivedMessageBuffer.ns, plaintext, this.remotePeer.id);
} catch (e) {
throw new Error(`Error occurred while verifying signed payload: ${e.message}`);
}
} }
} }

2
src/logger.ts Normal file
View File

@ -0,0 +1,2 @@
import debug from "debug";
export const logger = debug('libp2p:noise');

View File

@ -7,7 +7,8 @@ import pipe from 'it-pipe';
import lp from 'it-length-prefixed'; import lp from 'it-length-prefixed';
import { Handshake } from "./handshake"; import { Handshake } from "./handshake";
import { generateKeypair, int16BEDecode, int16BEEncode } from "./utils"; import { generateKeypair } from "./utils";
import { int16BEDecode, int16BEEncode } from "./encoder";
import { decryptStream, encryptStream } from "./crypto"; import { decryptStream, encryptStream } from "./crypto";
import { bytes } from "./@types/basic"; import { bytes } from "./@types/basic";
import { NoiseConnection, PeerId, KeyPair, SecureOutbound } from "./@types/libp2p"; import { NoiseConnection, PeerId, KeyPair, SecureOutbound } from "./@types/libp2p";
@ -46,8 +47,8 @@ export class Noise implements NoiseConnection {
*/ */
public async secureOutbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise<SecureOutbound> { public async secureOutbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise<SecureOutbound> {
const wrappedConnection = Wrap(connection); const wrappedConnection = Wrap(connection);
const libp2pPublicKey = localPeer.pubKey.marshal(); const libp2pPublicKey = localPeer.marshalPubKey();
const handshake = await this.performHandshake(wrappedConnection, true, libp2pPublicKey); const handshake = await this.performHandshake(wrappedConnection, true, libp2pPublicKey, remotePeer);
const conn = await this.createSecureConnection(wrappedConnection, handshake); const conn = await this.createSecureConnection(wrappedConnection, handshake);
return { return {
@ -65,8 +66,8 @@ export class Noise implements NoiseConnection {
*/ */
public async secureInbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise<SecureOutbound> { public async secureInbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise<SecureOutbound> {
const wrappedConnection = Wrap(connection); const wrappedConnection = Wrap(connection);
const libp2pPublicKey = localPeer.pubKey.marshal(); const libp2pPublicKey = localPeer.marshalPubKey();
const handshake = await this.performHandshake(wrappedConnection, false, libp2pPublicKey); const handshake = await this.performHandshake(wrappedConnection, false, libp2pPublicKey, remotePeer);
const conn = await this.createSecureConnection(wrappedConnection, handshake); const conn = await this.createSecureConnection(wrappedConnection, handshake);
return { return {
@ -79,13 +80,18 @@ export class Noise implements NoiseConnection {
connection: WrappedConnection, connection: WrappedConnection,
isInitiator: boolean, isInitiator: boolean,
libp2pPublicKey: bytes, libp2pPublicKey: bytes,
remotePeer: PeerId,
): Promise<Handshake> { ): Promise<Handshake> {
const prologue = Buffer.from(this.protocol); const prologue = Buffer.from(this.protocol);
const handshake = new Handshake(isInitiator, this.privateKey, libp2pPublicKey, prologue, this.staticKeys, connection); const handshake = new Handshake(isInitiator, this.privateKey, libp2pPublicKey, prologue, this.staticKeys, connection, remotePeer);
await handshake.propose(this.earlyData); try {
await handshake.propose();
await handshake.exchange(); await handshake.exchange();
await handshake.finish(); await handshake.finish(this.earlyData);
} catch (e) {
throw new Error(`Error occurred during handshake: ${e.message}`);
}
return handshake; return handshake;
} }

View File

@ -1,13 +1,11 @@
import { x25519, ed25519 } from 'bcrypto'; import { x25519, ed25519 } from 'bcrypto';
import protobuf from "protobufjs"; import protobuf from "protobufjs";
import { Buffer } from "buffer"; import { Buffer } from "buffer";
import debug from "debug"; import PeerId from "peer-id";
import * as crypto from 'libp2p-crypto';
import { KeyPair } from "./@types/libp2p"; import { KeyPair } from "./@types/libp2p";
import { bytes } from "./@types/basic"; import { bytes } from "./@types/basic";
import { MessageBuffer } from "./xx";
export const logger = debug('libp2p:noise');
export async function loadPayloadProto () { export async function loadPayloadProto () {
const payloadProtoBuf = await protobuf.load("protos/payload.proto"); const payloadProtoBuf = await protobuf.load("protos/payload.proto");
@ -70,26 +68,23 @@ export const getHandshakePayload = (publicKey: bytes ) => Buffer.concat([Buffer.
export const getEarlyDataPayload = (earlyData: bytes) => Buffer.concat([Buffer.from("noise-libp2p-early-data:"), earlyData]); export const getEarlyDataPayload = (earlyData: bytes) => Buffer.concat([Buffer.from("noise-libp2p-early-data:"), earlyData]);
export function encodeMessageBuffer(message: MessageBuffer): bytes { async function isValidPeerId(peerId: bytes, publicKeyProtobuf: bytes) {
return Buffer.concat([message.ne, message.ns, message.ciphertext]); const generatedPeerId = await PeerId.createFromPubKey(publicKeyProtobuf);
return generatedPeerId.id.equals(peerId);
} }
export function decodeMessageBuffer(message: bytes): MessageBuffer { export async function verifySignedPayload(noiseStaticKey: bytes, plaintext: bytes, peerId: bytes) {
return { const NoiseHandshakePayload = await loadPayloadProto();
ne: message.slice(0, 32), const receivedPayload = NoiseHandshakePayload.toObject(NoiseHandshakePayload.decode(plaintext));
ns: message.slice(32, 64),
ciphertext: message.slice(64, message.length), if (!(await isValidPeerId(peerId, receivedPayload.libp2pKey)) ) {
throw new Error("Peer ID doesn't match libp2p public key.");
}
const generatedPayload = getHandshakePayload(noiseStaticKey);
// Unmarshaling from PublicKey protobuf and taking key buffer only.
const publicKey = crypto.keys.unmarshalPublicKey(receivedPayload.libp2pKey).marshal();
if (!ed25519.verify(generatedPayload, receivedPayload.noiseStaticKeySignature, publicKey)) {
throw new Error("Static key doesn't match to peer that signed payload!");
} }
} }
export const int16BEEncode = (value, target, offset) => {
target = target || Buffer.allocUnsafe(2);
return target.writeInt16BE(value, offset);
};
int16BEEncode.bytes = 2;
export const int16BEDecode = data => {
if (data.length < 2) throw RangeError('Could not decode int16BE');
return data.readInt16BE(0);
};
int16BEDecode.bytes = 2;

View File

@ -191,7 +191,7 @@ export class XXHandshake {
return SHA256.digest(Buffer.from([...a, ...b])); return SHA256.digest(Buffer.from([...a, ...b]));
} }
private async encryptAndHash(ss: SymmetricState, plaintext: bytes): Promise<bytes> { private encryptAndHash(ss: SymmetricState, plaintext: bytes): bytes {
let ciphertext; let ciphertext;
if (this.hasKey(ss.cs)) { if (this.hasKey(ss.cs)) {
ciphertext = this.encryptWithAd(ss.cs, ss.h, plaintext); ciphertext = this.encryptWithAd(ss.cs, ss.h, plaintext);
@ -203,7 +203,7 @@ export class XXHandshake {
return ciphertext; return ciphertext;
} }
private async decryptAndHash(ss: SymmetricState, ciphertext: bytes): Promise<bytes> { private decryptAndHash(ss: SymmetricState, ciphertext: bytes): bytes {
let plaintext; let plaintext;
if (this.hasKey(ss.cs)) { if (this.hasKey(ss.cs)) {
plaintext = this.decryptWithAd(ss.cs, ss.h, ciphertext); plaintext = this.decryptWithAd(ss.cs, ss.h, ciphertext);
@ -223,38 +223,38 @@ export class XXHandshake {
return { cs1, cs2 }; return { cs1, cs2 };
} }
private async writeMessageA(hs: HandshakeState, payload: bytes): Promise<MessageBuffer> { private writeMessageA(hs: HandshakeState, payload: bytes): MessageBuffer {
const ns = Buffer.alloc(0); const ns = Buffer.alloc(0);
hs.e = generateKeypair(); hs.e = generateKeypair();
const ne = hs.e.publicKey; const ne = hs.e.publicKey;
this.mixHash(hs.ss, ne); this.mixHash(hs.ss, ne);
const ciphertext = await this.encryptAndHash(hs.ss, payload); const ciphertext = this.encryptAndHash(hs.ss, payload);
return {ne, ns, ciphertext}; return {ne, ns, ciphertext};
} }
private async writeMessageB(hs: HandshakeState, payload: bytes): Promise<MessageBuffer> { private writeMessageB(hs: HandshakeState, payload: bytes): MessageBuffer {
hs.e = generateKeypair(); hs.e = generateKeypair();
const ne = hs.e.publicKey; const ne = hs.e.publicKey;
this.mixHash(hs.ss, ne); 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.re));
const spk = Buffer.from(hs.s.publicKey); const spk = Buffer.from(hs.s.publicKey);
const ns = await this.encryptAndHash(hs.ss, spk); 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.re));
const ciphertext = await this.encryptAndHash(hs.ss, payload); const ciphertext = this.encryptAndHash(hs.ss, payload);
return { ne, ns, ciphertext }; return { ne, ns, ciphertext };
} }
private async writeMessageC(hs: HandshakeState, payload: bytes) { private writeMessageC(hs: HandshakeState, payload: bytes) {
const spk = Buffer.from(hs.s.publicKey); const spk = Buffer.from(hs.s.publicKey);
const ns = await this.encryptAndHash(hs.ss, spk); 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.re));
const ciphertext = await this.encryptAndHash(hs.ss, payload); const ciphertext = this.encryptAndHash(hs.ss, payload);
const ne = this.createEmptyKey(); const ne = this.createEmptyKey();
const messageBuffer: MessageBuffer = {ne, ns, ciphertext}; const messageBuffer: MessageBuffer = {ne, ns, ciphertext};
const { cs1, cs2 } = this.split(hs.ss); const { cs1, cs2 } = this.split(hs.ss);
@ -262,7 +262,7 @@ export class XXHandshake {
return { h: hs.ss.h, messageBuffer, cs1, cs2 }; return { h: hs.ss.h, messageBuffer, cs1, cs2 };
} }
private async writeMessageRegular(cs: CipherState, payload: bytes): Promise<MessageBuffer> { private writeMessageRegular(cs: CipherState, payload: bytes): MessageBuffer {
const ciphertext = this.encryptWithAd(cs, Buffer.alloc(0), payload); const ciphertext = this.encryptWithAd(cs, Buffer.alloc(0), payload);
const ne = this.createEmptyKey(); const ne = this.createEmptyKey();
const ns = Buffer.alloc(0); const ns = Buffer.alloc(0);
@ -270,16 +270,16 @@ export class XXHandshake {
return { ne, ns, ciphertext }; return { ne, ns, ciphertext };
} }
private async readMessageA(hs: HandshakeState, message: MessageBuffer): Promise<bytes> { private readMessageA(hs: HandshakeState, message: MessageBuffer): bytes {
if (x25519.publicKeyVerify(message.ne)) { if (x25519.publicKeyVerify(message.ne)) {
hs.re = message.ne; hs.re = message.ne;
} }
this.mixHash(hs.ss, hs.re); this.mixHash(hs.ss, hs.re);
return await this.decryptAndHash(hs.ss, message.ciphertext); return this.decryptAndHash(hs.ss, message.ciphertext);
} }
private async readMessageB(hs: HandshakeState, message: MessageBuffer): Promise<bytes> { private readMessageB(hs: HandshakeState, message: MessageBuffer): bytes {
if (x25519.publicKeyVerify(message.ne)) { if (x25519.publicKeyVerify(message.ne)) {
hs.re = message.ne; hs.re = message.ne;
} }
@ -289,16 +289,16 @@ export class XXHandshake {
throw new Error("Handshake state `e` param is missing."); throw new Error("Handshake state `e` param is missing.");
} }
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.re)); this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.re));
const ns = await this.decryptAndHash(hs.ss, message.ns); const ns = this.decryptAndHash(hs.ss, message.ns);
if (ns.length === 32 && x25519.publicKeyVerify(message.ns)) { if (ns.length === 32 && x25519.publicKeyVerify(message.ns)) {
hs.rs = ns; hs.rs = ns;
} }
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs)); this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs));
return await this.decryptAndHash(hs.ss, message.ciphertext); return this.decryptAndHash(hs.ss, message.ciphertext);
} }
private async readMessageC(hs: HandshakeState, message: MessageBuffer) { private readMessageC(hs: HandshakeState, message: MessageBuffer) {
const ns = await this.decryptAndHash(hs.ss, message.ns); const ns = this.decryptAndHash(hs.ss, message.ns);
if (ns.length === 32 && x25519.publicKeyVerify(message.ns)) { if (ns.length === 32 && x25519.publicKeyVerify(message.ns)) {
hs.rs = ns; hs.rs = ns;
} }
@ -308,7 +308,7 @@ export class XXHandshake {
} }
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs)); this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs));
const plaintext = await this.decryptAndHash(hs.ss, message.ciphertext); const plaintext = this.decryptAndHash(hs.ss, message.ciphertext);
const { cs1, cs2 } = this.split(hs.ss); const { cs1, cs2 } = this.split(hs.ss);
return { h: hs.ss.h, plaintext, cs1, cs2 }; return { h: hs.ss.h, plaintext, cs1, cs2 };
@ -336,14 +336,14 @@ export class XXHandshake {
}; };
} }
public async sendMessage(session: NoiseSession, message: bytes): Promise<MessageBuffer> { public sendMessage(session: NoiseSession, message: bytes): MessageBuffer {
let messageBuffer: MessageBuffer; let messageBuffer: MessageBuffer;
if (session.mc.eqn(0)) { if (session.mc.eqn(0)) {
messageBuffer = await this.writeMessageA(session.hs, message); messageBuffer = this.writeMessageA(session.hs, message);
} else if (session.mc.eqn(1)) { } else if (session.mc.eqn(1)) {
messageBuffer = await this.writeMessageB(session.hs, message); messageBuffer = this.writeMessageB(session.hs, message);
} else if (session.mc.eqn(2)) { } else if (session.mc.eqn(2)) {
const { h, messageBuffer: resultingBuffer, cs1, cs2 } = await this.writeMessageC(session.hs, message); const { h, messageBuffer: resultingBuffer, cs1, cs2 } = this.writeMessageC(session.hs, message);
messageBuffer = resultingBuffer; messageBuffer = resultingBuffer;
session.h = h; session.h = h;
session.cs1 = cs1; session.cs1 = cs1;
@ -354,13 +354,13 @@ export class XXHandshake {
throw new Error("CS1 (cipher state) is not defined") throw new Error("CS1 (cipher state) is not defined")
} }
messageBuffer = await this.writeMessageRegular(session.cs1, message); messageBuffer = this.writeMessageRegular(session.cs1, message);
} else { } else {
if (!session.cs2) { if (!session.cs2) {
throw new Error("CS2 (cipher state) is not defined") throw new Error("CS2 (cipher state) is not defined")
} }
messageBuffer = await this.writeMessageRegular(session.cs2, message); messageBuffer = this.writeMessageRegular(session.cs2, message);
} }
} else { } else {
throw new Error("Session invalid.") throw new Error("Session invalid.")
@ -370,14 +370,14 @@ export class XXHandshake {
return messageBuffer; return messageBuffer;
} }
public async recvMessage(session: NoiseSession, message: MessageBuffer): Promise<bytes> { public recvMessage(session: NoiseSession, message: MessageBuffer): bytes {
let plaintext: bytes; let plaintext: bytes;
if (session.mc.eqn(0)) { if (session.mc.eqn(0)) {
plaintext = await this.readMessageA(session.hs, message); plaintext = this.readMessageA(session.hs, message);
} else if (session.mc.eqn(1)) { } else if (session.mc.eqn(1)) {
plaintext = await this.readMessageB(session.hs, message); plaintext = this.readMessageB(session.hs, message);
} else if (session.mc.eqn(2)) { } else if (session.mc.eqn(2)) {
const { h, plaintext: resultingPlaintext, cs1, cs2 } = await this.readMessageC(session.hs, message); const { h, plaintext: resultingPlaintext, cs1, cs2 } = this.readMessageC(session.hs, message);
plaintext = resultingPlaintext; plaintext = resultingPlaintext;
session.h = h; session.h = h;
session.cs1 = cs1; session.cs1 = cs1;
@ -387,12 +387,12 @@ export class XXHandshake {
if (!session.cs2) { if (!session.cs2) {
throw new Error("CS1 (cipher state) is not defined") throw new Error("CS1 (cipher state) is not defined")
} }
plaintext = await this.readMessageRegular(session.cs2, message); plaintext = this.readMessageRegular(session.cs2, message);
} else { } else {
if (!session.cs1) { if (!session.cs1) {
throw new Error("CS1 (cipher state) is not defined") throw new Error("CS1 (cipher state) is not defined")
} }
plaintext = await this.readMessageRegular(session.cs1, message); plaintext = this.readMessageRegular(session.cs1, message);
} }
} else { } else {
throw new Error("Session invalid."); throw new Error("Session invalid.");

View File

@ -1,15 +1,23 @@
import {assert} from "chai"; import {assert, expect} from "chai";
import Duplex from 'it-pair/duplex'; import Duplex from 'it-pair/duplex';
import {Buffer} from "buffer"; import {Buffer} from "buffer";
import Wrap from "it-pb-rpc"; import Wrap from "it-pb-rpc";
import {Handshake} from "../src/handshake"; import {Handshake} from "../src/handshake";
import {generateKeypair} from "../src/utils"; import {generateKeypair} from "../src/utils";
import {createPeerIds} from "./fixtures/peer"; import {createPeerIdsFromFixtures} from "./fixtures/peer";
import {getKeyPairFromPeerId} from "./utils";
describe("Handshake", () => { describe("Handshake", () => {
let peerA, peerB, fakePeer;
before(async () => {
[peerA, peerB, fakePeer] = await createPeerIdsFromFixtures(3);
});
it("should propose, exchange and finish handshake", async() => { it("should propose, exchange and finish handshake", async() => {
try {
const duplex = Duplex(); const duplex = Duplex();
const connectionFrom = Wrap(duplex[0]); const connectionFrom = Wrap(duplex[0]);
const connectionTo = Wrap(duplex[1]); const connectionTo = Wrap(duplex[1]);
@ -17,15 +25,12 @@ describe("Handshake", () => {
const prologue = Buffer.from('/noise'); const prologue = Buffer.from('/noise');
const staticKeysInitiator = generateKeypair(); const staticKeysInitiator = generateKeypair();
const staticKeysResponder = generateKeypair(); const staticKeysResponder = generateKeypair();
const [peerA, peerB] = await createPeerIds(2);
const initiatorPrivKey = peerA.privKey.marshal().slice(0, 32); const { privateKey: initiatorPrivKey, publicKey: initiatorPubKey } = getKeyPairFromPeerId(peerA);
const initiatorPubKey = peerA.pubKey.marshal(); const handshakeInitator = new Handshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom, peerB);
const handshakeInitator = new Handshake(true, initiatorPrivKey, initiatorPubKey, prologue, staticKeysInitiator, connectionFrom);
const responderPrivKey = peerB.privKey.marshal().slice(0, 32); const { privateKey: responderPrivKey, publicKey: responderPubKey } = getKeyPairFromPeerId(peerB);
const responderPubKey = peerB.pubKey.marshal(); const handshakeResponder = new Handshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo, peerA);
const handshakeResponder = new Handshake(false, responderPrivKey, responderPubKey, prologue, staticKeysResponder, connectionTo);
await handshakeInitator.propose(); await handshakeInitator.propose();
await handshakeResponder.propose(); await handshakeResponder.propose();
@ -51,5 +56,67 @@ describe("Handshake", () => {
const encrypted = handshakeInitator.encrypt(Buffer.from("encryptthis"), handshakeInitator.session); const encrypted = handshakeInitator.encrypt(Buffer.from("encryptthis"), handshakeInitator.session);
const decrypted = handshakeResponder.decrypt(encrypted, handshakeResponder.session); const decrypted = handshakeResponder.decrypt(encrypted, handshakeResponder.session);
assert(decrypted.equals(Buffer.from("encryptthis"))); assert(decrypted.equals(Buffer.from("encryptthis")));
} catch (e) {
assert(false, e.message);
}
});
it("Initiator should fail to exchange handshake if given wrong public key in payload", 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 handshakeInitator = new Handshake(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);
await handshakeInitator.propose();
await handshakeResponder.propose();
await handshakeResponder.exchange();
await handshakeInitator.exchange();
assert(false, "Should throw exception");
} catch (e) {
expect(e.message).equals("Error occurred while verifying signed payload: Peer ID doesn't match libp2p public key.")
}
});
it("Responder should fail to exchange handshake if given wrong public key in payload", 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 handshakeInitator = new Handshake(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);
await handshakeInitator.propose();
await handshakeResponder.propose();
await handshakeResponder.exchange();
await handshakeInitator.exchange();
await handshakeInitator.finish();
await handshakeResponder.finish();
assert(false, "Should throw exception");
} catch (e) {
expect(e.message).equals("Error occurred while verifying signed payload: Peer ID doesn't match libp2p public key.")
}
}); });
}); });

View File

@ -2,32 +2,31 @@ import { expect, assert } from "chai";
import DuplexPair from 'it-pair/duplex'; import DuplexPair from 'it-pair/duplex';
import { Noise } from "../src"; import { Noise } from "../src";
import { generateEd25519Keys } from "./utils"; import {createPeerIdsFromFixtures} from "./fixtures/peer";
import {createPeerIds, createPeerIdsFromFixtures} from "./fixtures/peer";
import Wrap from "it-pb-rpc"; import Wrap from "it-pb-rpc";
import {Handshake} from "../src/handshake"; import {Handshake} from "../src/handshake";
import { import {
createHandshakePayload, createHandshakePayload,
decodeMessageBuffer,
encodeMessageBuffer,
generateKeypair, generateKeypair,
getHandshakePayload, getHandshakePayload,
signPayload signPayload
} from "../src/utils"; } from "../src/utils";
import { decodeMessageBuffer, encodeMessageBuffer } from "../src/encoder";
import {XXHandshake} from "../src/xx"; import {XXHandshake} from "../src/xx";
import {Buffer} from "buffer"; import {Buffer} from "buffer";
import {getKeyPairFromPeerId} from "./utils";
describe("Noise", () => { describe("Noise", () => {
let remotePeer, localPeer; let remotePeer, localPeer;
before(async () => { before(async () => {
[localPeer, remotePeer] = await createPeerIds(2); [localPeer, remotePeer] = await createPeerIdsFromFixtures(2);
}); });
it("should communicate through encrypted streams", async() => { it("should communicate through encrypted streams", async() => {
const libp2pInitPrivKey = localPeer.privKey.marshal().slice(0, 32); try {
const libp2pRespPrivKey = remotePeer.privKey.marshal().slice(0, 32); const { privateKey: libp2pInitPrivKey } = getKeyPairFromPeerId(localPeer);
const { privateKey: libp2pRespPrivKey } = getKeyPairFromPeerId(remotePeer);
const noiseInit = new Noise(libp2pInitPrivKey); const noiseInit = new Noise(libp2pInitPrivKey);
const noiseResp = new Noise(libp2pRespPrivKey); const noiseResp = new Noise(libp2pRespPrivKey);
@ -42,11 +41,14 @@ describe("Noise", () => {
wrappedOutbound.writeLP(Buffer.from("test")); wrappedOutbound.writeLP(Buffer.from("test"));
const response = await wrappedInbound.readLP(); const response = await wrappedInbound.readLP();
expect(response.toString()).equal("test"); expect(response.toString()).equal("test");
} catch (e) {
assert(false, e.message);
}
}); });
it("should test that secureOutbound is spec compliant", async() => { it("should test that secureOutbound is spec compliant", async() => {
const libp2pPrivKey = localPeer.privKey.marshal().slice(0, 32); const { privateKey: libp2pInitPrivKey } = getKeyPairFromPeerId(localPeer);
const noiseInit = new Noise(libp2pPrivKey); const noiseInit = new Noise(libp2pInitPrivKey);
const [inboundConnection, outboundConnection] = DuplexPair(); const [inboundConnection, outboundConnection] = DuplexPair();
const [outbound, { wrapped, handshake }] = await Promise.all([ const [outbound, { wrapped, handshake }] = await Promise.all([
@ -56,28 +58,30 @@ describe("Noise", () => {
const prologue = Buffer.from('/noise'); const prologue = Buffer.from('/noise');
const staticKeys = generateKeypair(); const staticKeys = generateKeypair();
const xx = new XXHandshake(); const xx = new XXHandshake();
const libp2pPubKey = remotePeer.pubKey.marshal().slice(32, 64); const { privateKey: libp2pPrivKey, publicKey: libp2pPubKey } = getKeyPairFromPeerId(remotePeer);
const handshake = new Handshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, xx);
const handshake = new Handshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, xx);
let receivedMessageBuffer = decodeMessageBuffer((await wrapped.readLP()).slice()); let receivedMessageBuffer = decodeMessageBuffer((await wrapped.readLP()).slice());
// The first handshake message contains the initiator's ephemeral public key // The first handshake message contains the initiator's ephemeral public key
expect(receivedMessageBuffer.ne.length).equal(32); expect(receivedMessageBuffer.ne.length).equal(32);
await xx.recvMessage(handshake.session, receivedMessageBuffer); xx.recvMessage(handshake.session, receivedMessageBuffer);
// Stage 1 // Stage 1
const signedPayload = signPayload(libp2pPrivKey, getHandshakePayload(staticKeys.publicKey)); const signedPayload = signPayload(libp2pPrivKey, getHandshakePayload(staticKeys.publicKey));
const handshakePayload = await createHandshakePayload(libp2pPubKey, libp2pPrivKey, signedPayload); const handshakePayload = await createHandshakePayload(libp2pPubKey, libp2pPrivKey, signedPayload);
const messageBuffer = await xx.sendMessage(handshake.session, handshakePayload); const messageBuffer = xx.sendMessage(handshake.session, handshakePayload);
wrapped.writeLP(encodeMessageBuffer(messageBuffer)); wrapped.writeLP(encodeMessageBuffer(messageBuffer));
// Stage 2 - finish handshake // Stage 2 - finish handshake
receivedMessageBuffer = decodeMessageBuffer((await wrapped.readLP()).slice()); receivedMessageBuffer = decodeMessageBuffer((await wrapped.readLP()).slice());
await xx.recvMessage(handshake.session, receivedMessageBuffer); xx.recvMessage(handshake.session, receivedMessageBuffer);
return { wrapped, handshake }; return {wrapped, handshake};
})(), })(),
]); ]);
try {
const wrappedOutbound = Wrap(outbound.conn); const wrappedOutbound = Wrap(outbound.conn);
wrappedOutbound.write(Buffer.from("test")); wrappedOutbound.write(Buffer.from("test"));
@ -88,5 +92,8 @@ describe("Noise", () => {
const decrypted = handshake.decrypt(data, handshake.session); const decrypted = handshake.decrypt(data, handshake.session);
// Decrypted data should match // Decrypted data should match
assert(decrypted.equals(Buffer.from("test"))); assert(decrypted.equals(Buffer.from("test")));
} catch (e) {
assert(false, e.message);
}
}) })
}); });

View File

@ -1,5 +1,13 @@
import * as crypto from 'libp2p-crypto'; import * as crypto from 'libp2p-crypto';
import {KeyPair, PeerId} from "../src/@types/libp2p";
export async function generateEd25519Keys() { export async function generateEd25519Keys() {
return await crypto.keys.generateKeyPair('ed25519'); return await crypto.keys.generateKeyPair('ed25519');
} }
export function getKeyPairFromPeerId(peerId: PeerId): KeyPair {
return {
privateKey: peerId.privKey.marshal().slice(0, 32),
publicKey: peerId.marshalPubKey(),
}
}

View File

@ -48,25 +48,26 @@ describe("Index", () => {
const respSignedPayload = await libp2pRespKeys.sign(getHandshakePayload(kpResp.publicKey)); const respSignedPayload = await libp2pRespKeys.sign(getHandshakePayload(kpResp.publicKey));
// initiator: new XX noise session // initiator: new XX noise session
const nsInit = await xx.initSession(true, prologue, kpInit); const nsInit = xx.initSession(true, prologue, kpInit);
// responder: new XX noise session // responder: new XX noise session
const nsResp = await xx.initSession(false, prologue, kpResp); const nsResp = xx.initSession(false, prologue, kpResp);
/* STAGE 0 */ /* STAGE 0 */
// initiator creates payload // initiator creates payload
const libp2pInitPrivKey = libp2pInitKeys.marshal().slice(0, 32); const libp2pInitPrivKey = libp2pInitKeys.marshal().slice(0, 32);
const libp2pInitPubKey = libp2pInitKeys.marshal().slice(32, 64); const libp2pInitPubKey = libp2pInitKeys.marshal().slice(32, 64);
const payloadInitEnc = await createHandshakePayload(libp2pInitPubKey, libp2pInitPrivKey, initSignedPayload); const payloadInitEnc = await createHandshakePayload(libp2pInitPubKey, libp2pInitPrivKey, initSignedPayload);
// initiator sends message // initiator sends message
const message = Buffer.concat([Buffer.alloc(0), payloadInitEnc]); const message = Buffer.concat([Buffer.alloc(0), payloadInitEnc]);
const messageBuffer = await xx.sendMessage(nsInit, message); const messageBuffer = xx.sendMessage(nsInit, message);
expect(messageBuffer.ne.length).not.equal(0); expect(messageBuffer.ne.length).not.equal(0);
// responder receives message // responder receives message
const plaintext = await xx.recvMessage(nsResp, messageBuffer); const plaintext = xx.recvMessage(nsResp, messageBuffer);
console.log("Stage 0 responder payload: ", plaintext); console.log("Stage 0 responder payload: ", plaintext);
/* STAGE 1 */ /* STAGE 1 */
@ -77,22 +78,22 @@ describe("Index", () => {
const payloadRespEnc = await createHandshakePayload(libp2pRespPubKey, libp2pRespPrivKey, respSignedPayload); const payloadRespEnc = await createHandshakePayload(libp2pRespPubKey, libp2pRespPrivKey, respSignedPayload);
const message1 = Buffer.concat([message, payloadRespEnc]); const message1 = Buffer.concat([message, payloadRespEnc]);
const messageBuffer2 = await xx.sendMessage(nsResp, message1); const messageBuffer2 = xx.sendMessage(nsResp, message1);
expect(messageBuffer2.ne.length).not.equal(0); expect(messageBuffer2.ne.length).not.equal(0);
expect(messageBuffer2.ns.length).not.equal(0); expect(messageBuffer2.ns.length).not.equal(0);
// initiator receive payload // initiator receive payload
const plaintext2 = await xx.recvMessage(nsInit, messageBuffer2); const plaintext2 = xx.recvMessage(nsInit, messageBuffer2);
console.log("Stage 1 responder payload: ", plaintext2); console.log("Stage 1 responder payload: ", plaintext2);
/* STAGE 2 */ /* STAGE 2 */
// initiator send message // initiator send message
const messageBuffer3 = await xx.sendMessage(nsInit, Buffer.alloc(0)); const messageBuffer3 = xx.sendMessage(nsInit, Buffer.alloc(0));
// responder receive message // responder receive message
const plaintext3 = await xx.recvMessage(nsResp, messageBuffer3); const plaintext3 = xx.recvMessage(nsResp, messageBuffer3);
console.log("Stage 2 responder payload: ", plaintext3); console.log("Stage 2 responder payload: ", plaintext3);
assert(nsInit.cs1.k.equals(nsResp.cs1.k)); assert(nsInit.cs1.k.equals(nsResp.cs1.k));