Merge pull request #67 from NodeFactoryIo/mpetrunic/lint-fix

Fix lint errors and tests
This commit is contained in:
Marin Petrunić 2020-06-23 17:36:45 +02:00 committed by GitHub
commit e01d8e293a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1504 additions and 1548 deletions

2
.nvmrc
View File

@ -1 +1 @@
12.4.0
12

View File

@ -5,6 +5,9 @@ stages:
- test
- cov
env:
- YARN_GPG=no
node_js:
- '12'
- '14'
@ -15,7 +18,9 @@ os:
- windows
script: npx nyc -s yarn run test:node --bail
after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
after_success:
- npm install -g travis-deploy-once
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then travis-deploy-once "npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov"; fi
jobs:
include:
@ -23,18 +28,19 @@ jobs:
script:
- yarn aegir dep-check
- yarn run lint
- yarn run build
- stage: test
name: chrome
addons:
chrome: stable
script: npx aegir test -t browser -t webworker --ts
script: npx aegir test -t browser -t webworker --node true --ts
- stage: test
name: firefox
addons:
firefox: latest
script: npx aegir test -t browser -t webworker --ts -- --browsers FirefoxHeadless
script: npx aegir test -t browser -t webworker --ts --node true -- --browsers FirefoxHeadless
- stage: test
name: electron-main
@ -46,7 +52,7 @@ jobs:
name: electron-renderer
os: osx
script:
- npx aegir test -t electron-renderer --bail --ts
- npx aegir test -t electron-renderer --node true --bail --ts
notifications:
email: false

View File

@ -2,8 +2,10 @@
"name": "libp2p-noise",
"version": "1.1.2",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"files": [
"dist"
"dist",
"src"
],
"repository": "git@github.com:NodeFactoryIo/js-libp2p-noise.git",
"author": "NodeFactory <info@nodefactory.io>",
@ -16,17 +18,19 @@
"scripts": {
"build": "aegir build --ts",
"lint": "aegir lint --ts",
"test": "aegir test --ts",
"lint:fix": "aegir lint --ts --fix",
"test": "aegir test --ts --node true",
"test:node": "aegir test -t node --ts",
"test:browser": "aegir test -t browser --ts",
"test:browser": "aegir test -t browser --node true --ts",
"proto:gen": "pbjs -t static-module -o ./src/proto/payload.js ./src/proto/payload.proto && pbts -o ./src/proto/payload.d.ts ./src/proto/payload.js && yarn run lint --fix"
},
"devDependencies": {
"@types/bl": "^2.1.0",
"@types/chai": "^4.2.4",
"@types/mocha": "^5.2.7",
"aegir": "ipfs/aegir#feat/cosmiconfig",
"aegir": "25.0.0",
"chai": "^4.2.0",
"karma-mocha-webworker": "^1.3.0",
"mocha": "^6.2.2",
"sinon": "^8.1.0"
},
@ -47,6 +51,12 @@
"bn.js": "4.4.0"
},
"eslintConfig": {
"extends": "./node_modules/aegir/src/config/eslintrc-ts.js"
"extends": "./node_modules/aegir/src/config/eslintrc-ts.js",
"rules": {
"@typescript-eslint/no-unused-vars": "error"
},
"ignorePatterns": [
"src/proto/payload.js"
]
}
}

View File

@ -1,7 +1,6 @@
declare module "it-length-prefixed" {
/* eslint-disable @typescript-eslint/interface-name-prefix */
import BufferList from "bl";
import {Buffer} from "buffer"
declare module 'it-length-prefixed' {
import BufferList from 'bl'
import { Buffer } from 'buffer'
interface LengthDecoderFunction {
(data: Buffer | BufferList): number;
@ -9,7 +8,7 @@ declare module "it-length-prefixed" {
}
interface LengthEncoderFunction {
(value: Buffer, target: number, offset: number): number|Buffer;
(value: number, target: Buffer, offset: number): number|Buffer;
bytes: number;
}
@ -33,7 +32,7 @@ declare module "it-length-prefixed" {
MAX_DATA_LENGTH: number;
}
export const encode: Encoder;
export const decode: Decoder;
export const encode: Encoder
export const decode: Decoder
}

View File

@ -1,6 +1,4 @@
export const NOISE_MSG_MAX_LENGTH_BYTES = 65535;
export const NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG = NOISE_MSG_MAX_LENGTH_BYTES - 16;
export const DUMP_SESSION_KEYS = process.env.DUMP_SESSION_KEYS;
export const NOISE_MSG_MAX_LENGTH_BYTES = 65535
export const NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG = NOISE_MSG_MAX_LENGTH_BYTES - 16
export const DUMP_SESSION_KEYS = process.env.DUMP_SESSION_KEYS

View File

@ -1,49 +1,48 @@
import { Buffer } from "buffer";
import {IHandshake} from "./@types/handshake-interface";
import {NOISE_MSG_MAX_LENGTH_BYTES, NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG} from "./constants";
import { Buffer } from 'buffer'
import { IHandshake } from './@types/handshake-interface'
import { NOISE_MSG_MAX_LENGTH_BYTES, NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG } from './constants'
interface IReturnEncryptionWrapper {
(source: Iterable<Uint8Array>): AsyncIterableIterator<Uint8Array>;
}
// Returns generator that encrypts payload from the user
export function encryptStream(handshake: IHandshake): IReturnEncryptionWrapper {
export function encryptStream (handshake: IHandshake): IReturnEncryptionWrapper {
return async function * (source) {
for await (const chunk of source) {
const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length);
const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length)
for (let i = 0; i < chunkBuffer.length; i += NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG) {
let end = i + NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG;
let end = i + NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG
if (end > chunkBuffer.length) {
end = chunkBuffer.length;
end = chunkBuffer.length
}
const data = handshake.encrypt(chunkBuffer.slice(i, end), handshake.session);
yield data;
const data = handshake.encrypt(chunkBuffer.slice(i, end), handshake.session)
yield data
}
}
}
}
// Decrypt received payload to the user
export function decryptStream(handshake: IHandshake): IReturnEncryptionWrapper {
export function decryptStream (handshake: IHandshake): IReturnEncryptionWrapper {
return async function * (source) {
for await (const chunk of source) {
const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length);
const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length)
for (let i = 0; i < chunkBuffer.length; i += NOISE_MSG_MAX_LENGTH_BYTES) {
let end = i + NOISE_MSG_MAX_LENGTH_BYTES;
let end = i + NOISE_MSG_MAX_LENGTH_BYTES
if (end > chunkBuffer.length) {
end = chunkBuffer.length;
end = chunkBuffer.length
}
const chunk = chunkBuffer.slice(i, end);
const {plaintext: decrypted, valid} = await handshake.decrypt(chunk, handshake.session);
if(!valid) {
throw new Error("Failed to validate decrypted chunk");
const chunk = chunkBuffer.slice(i, end)
const { plaintext: decrypted, valid } = await handshake.decrypt(chunk, handshake.session)
if (!valid) {
throw new Error('Failed to validate decrypted chunk')
}
yield decrypted;
yield decrypted
}
}
}

View File

@ -1,66 +1,67 @@
import {Buffer} from "buffer";
import {bytes} from "./@types/basic";
import {MessageBuffer} from "./@types/handshake";
import { Buffer } from 'buffer'
import { bytes } from './@types/basic'
import { MessageBuffer } from './@types/handshake'
import BufferList from 'bl'
export const uint16BEEncode = (value, target, offset) => {
target = target || Buffer.allocUnsafe(2);
target.writeUInt16BE(value, offset);
return target;
};
uint16BEEncode.bytes = 2;
export const uint16BEEncode = (value: number, target: Buffer, offset: number): Buffer => {
target = target || Buffer.allocUnsafe(2)
target.writeUInt16BE(value, offset)
return target
}
uint16BEEncode.bytes = 2
export const uint16BEDecode = data => {
if (data.length < 2) throw RangeError('Could not decode int16BE');
return data.readUInt16BE(0);
};
uint16BEDecode.bytes = 2;
export const uint16BEDecode = (data: Buffer | BufferList): number => {
if (data.length < 2) throw RangeError('Could not decode int16BE')
return data.readUInt16BE(0)
}
uint16BEDecode.bytes = 2
// Note: IK and XX encoder usage is opposite (XX uses in stages encode0 where IK uses encode1)
export function encode0(message: MessageBuffer): bytes {
return Buffer.concat([message.ne, message.ciphertext]);
export function encode0 (message: MessageBuffer): bytes {
return Buffer.concat([message.ne, message.ciphertext])
}
export function encode1(message: MessageBuffer): bytes {
return Buffer.concat([message.ne, message.ns, message.ciphertext]);
export function encode1 (message: MessageBuffer): bytes {
return Buffer.concat([message.ne, message.ns, message.ciphertext])
}
export function encode2(message: MessageBuffer): bytes {
return Buffer.concat([message.ns, message.ciphertext]);
export function encode2 (message: MessageBuffer): bytes {
return Buffer.concat([message.ns, message.ciphertext])
}
export function decode0(input: bytes): MessageBuffer {
export function decode0 (input: bytes): MessageBuffer {
if (input.length < 32) {
throw new Error("Cannot decode stage 0 MessageBuffer: length less than 32 bytes.");
throw new Error('Cannot decode stage 0 MessageBuffer: length less than 32 bytes.')
}
return {
ne: input.slice(0, 32),
ciphertext: input.slice(32, input.length),
ns: Buffer.alloc(0),
ns: Buffer.alloc(0)
}
}
export function decode1(input: bytes): MessageBuffer {
export function decode1 (input: bytes): MessageBuffer {
if (input.length < 80) {
throw new Error("Cannot decode stage 1 MessageBuffer: length less than 80 bytes.");
throw new Error('Cannot decode stage 1 MessageBuffer: length less than 80 bytes.')
}
return {
ne: input.slice(0, 32),
ns: input.slice(32, 80),
ciphertext: input.slice(80, input.length),
ciphertext: input.slice(80, input.length)
}
}
export function decode2(input: bytes): MessageBuffer {
export function decode2 (input: bytes): MessageBuffer {
if (input.length < 48) {
throw new Error("Cannot decode stage 2 MessageBuffer: length less than 48 bytes.");
throw new Error('Cannot decode stage 2 MessageBuffer: length less than 48 bytes.')
}
return {
ne: Buffer.alloc(0),
ns: input.slice(0, 48),
ciphertext: input.slice(48, input.length),
ciphertext: input.slice(48, input.length)
}
}

View File

@ -1,10 +1,12 @@
import BufferList from 'bl'
export class FailedIKError extends Error {
public initialMsg;
public initialMsg: string|BufferList|Buffer;
constructor(initialMsg, message?: string) {
super(message);
constructor (initialMsg: string|BufferList|Buffer, message?: string) {
super(message)
this.initialMsg = initialMsg;
this.name = "FailedIKhandshake";
this.initialMsg = initialMsg
this.name = 'FailedIKhandshake'
}
};
}

View File

@ -1,22 +1,22 @@
import {WrappedConnection} from "./noise";
import {IK} from "./handshakes/ik";
import {NoiseSession} from "./@types/handshake";
import {bytes, bytes32} from "./@types/basic";
import {KeyPair} from "./@types/libp2p";
import {IHandshake} from "./@types/handshake-interface";
import {Buffer} from "buffer";
import {decode0, decode1, encode0, encode1} from "./encoder";
import {decodePayload, getPeerIdFromPayload, verifySignedPayload} from "./utils";
import {FailedIKError} from "./errors";
import { WrappedConnection } from './noise'
import { IK } from './handshakes/ik'
import { NoiseSession } from './@types/handshake'
import { bytes, bytes32 } from './@types/basic'
import { KeyPair } from './@types/libp2p'
import { IHandshake } from './@types/handshake-interface'
import { Buffer } from 'buffer'
import { decode0, decode1, encode0, encode1 } from './encoder'
import { decodePayload, getPeerIdFromPayload, verifySignedPayload } from './utils'
import { FailedIKError } from './errors'
import {
logger,
logger,
logLocalStaticKeys,
logRemoteStaticKey,
logLocalEphemeralKeys,
logRemoteEphemeralKey,
logRemoteStaticKey,
logLocalEphemeralKeys,
logRemoteEphemeralKey,
logCipherState
} from "./logger";
import PeerId from "peer-id";
} from './logger'
import PeerId from 'peer-id'
export class IKHandshake implements IHandshake {
public isInitiator: boolean;
@ -30,7 +30,7 @@ export class IKHandshake implements IHandshake {
private connection: WrappedConnection;
private ik: IK;
constructor(
constructor (
isInitiator: boolean,
payload: bytes,
prologue: bytes32,
@ -38,118 +38,118 @@ export class IKHandshake implements IHandshake {
connection: WrappedConnection,
remoteStaticKey: bytes,
remotePeer?: PeerId,
handshake?: IK,
handshake?: IK
) {
this.isInitiator = isInitiator;
this.payload = Buffer.from(payload);
this.prologue = prologue;
this.staticKeypair = staticKeypair;
this.connection = connection;
if(remotePeer) {
this.remotePeer = remotePeer;
this.isInitiator = isInitiator
this.payload = Buffer.from(payload)
this.prologue = prologue
this.staticKeypair = staticKeypair
this.connection = connection
if (remotePeer) {
this.remotePeer = remotePeer
}
this.ik = handshake || new IK();
this.session = this.ik.initSession(this.isInitiator, this.prologue, this.staticKeypair, remoteStaticKey);
this.ik = handshake || new IK()
this.session = this.ik.initSession(this.isInitiator, this.prologue, this.staticKeypair, remoteStaticKey)
this.remoteEarlyData = Buffer.alloc(0)
}
public async stage0(): Promise<void> {
public async stage0 (): Promise<void> {
logLocalStaticKeys(this.session.hs.s)
logRemoteStaticKey(this.session.hs.rs)
if (this.isInitiator) {
logger("IK Stage 0 - Initiator sending message...");
const messageBuffer = this.ik.sendMessage(this.session, this.payload);
this.connection.writeLP(encode1(messageBuffer));
logger("IK Stage 0 - Initiator sent message.");
logger('IK Stage 0 - Initiator sending message...')
const messageBuffer = this.ik.sendMessage(this.session, this.payload)
this.connection.writeLP(encode1(messageBuffer))
logger('IK Stage 0 - Initiator sent message.')
logLocalEphemeralKeys(this.session.hs.e)
} else {
logger("IK Stage 0 - Responder receiving message...");
const receivedMsg = await this.connection.readLP();
logger('IK Stage 0 - Responder receiving message...')
const receivedMsg = await this.connection.readLP()
try {
const receivedMessageBuffer = decode1(receivedMsg.slice());
const {plaintext, valid} = this.ik.recvMessage(this.session, receivedMessageBuffer);
if(!valid) {
throw new Error("ik handshake stage 0 decryption validation fail");
const receivedMessageBuffer = decode1(receivedMsg.slice())
const { plaintext, valid } = this.ik.recvMessage(this.session, receivedMessageBuffer)
if (!valid) {
throw new Error('ik handshake stage 0 decryption validation fail')
}
logger("IK Stage 0 - Responder got message, going to verify payload.");
const decodedPayload = await decodePayload(plaintext);
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload);
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer);
this.setRemoteEarlyData(decodedPayload.data);
logger("IK Stage 0 - Responder successfully verified payload!");
logger('IK Stage 0 - Responder got message, going to verify payload.')
const decodedPayload = await decodePayload(plaintext)
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
this.setRemoteEarlyData(decodedPayload.data)
logger('IK Stage 0 - Responder successfully verified payload!')
logRemoteEphemeralKey(this.session.hs.re)
} catch (e) {
logger("Responder breaking up with IK handshake in stage 0.");
logger('Responder breaking up with IK handshake in stage 0.')
throw new FailedIKError(receivedMsg, `Error occurred while verifying initiator's signed payload: ${e.message}`);
throw new FailedIKError(receivedMsg, `Error occurred while verifying initiator's signed payload: ${e.message}`)
}
}
}
public async stage1(): Promise<void> {
public async stage1 (): Promise<void> {
if (this.isInitiator) {
logger("IK Stage 1 - Initiator receiving message...");
const receivedMsg = (await this.connection.readLP()).slice();
const receivedMessageBuffer = decode0(Buffer.from(receivedMsg));
const {plaintext, valid} = this.ik.recvMessage(this.session, receivedMessageBuffer);
logger("IK Stage 1 - Initiator got message, going to verify payload.");
logger('IK Stage 1 - Initiator receiving message...')
const receivedMsg = (await this.connection.readLP()).slice()
const receivedMessageBuffer = decode0(Buffer.from(receivedMsg))
const { plaintext, valid } = this.ik.recvMessage(this.session, receivedMessageBuffer)
logger('IK Stage 1 - Initiator got message, going to verify payload.')
try {
if(!valid) {
throw new Error("ik stage 1 decryption validation fail");
if (!valid) {
throw new Error('ik stage 1 decryption validation fail')
}
const decodedPayload = await decodePayload(plaintext);
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload);
await verifySignedPayload(receivedMessageBuffer.ns.slice(0, 32), decodedPayload, this.remotePeer);
this.setRemoteEarlyData(decodedPayload.data);
logger("IK Stage 1 - Initiator successfully verified payload!");
const decodedPayload = await decodePayload(plaintext)
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
await verifySignedPayload(receivedMessageBuffer.ns.slice(0, 32), decodedPayload, this.remotePeer)
this.setRemoteEarlyData(decodedPayload.data)
logger('IK Stage 1 - Initiator successfully verified payload!')
logRemoteEphemeralKey(this.session.hs.re)
} catch (e) {
logger("Initiator breaking up with IK handshake in stage 1.");
throw new FailedIKError(receivedMsg, `Error occurred while verifying responder's signed payload: ${e.message}`);
logger('Initiator breaking up with IK handshake in stage 1.')
throw new FailedIKError(receivedMsg, `Error occurred while verifying responder's signed payload: ${e.message}`)
}
} else {
logger("IK Stage 1 - Responder sending message...");
const messageBuffer = this.ik.sendMessage(this.session, this.payload);
this.connection.writeLP(encode0(messageBuffer));
logger("IK Stage 1 - Responder sent message...");
logger('IK Stage 1 - Responder sending message...')
const messageBuffer = this.ik.sendMessage(this.session, this.payload)
this.connection.writeLP(encode0(messageBuffer))
logger('IK Stage 1 - Responder sent message...')
logLocalEphemeralKeys(this.session.hs.e)
}
logCipherState(this.session)
}
public decrypt(ciphertext: bytes, session: NoiseSession): {plaintext: bytes; valid: boolean} {
const cs = this.getCS(session, false);
return this.ik.decryptWithAd(cs, Buffer.alloc(0), ciphertext);
public decrypt (ciphertext: bytes, session: NoiseSession): {plaintext: bytes; valid: boolean} {
const cs = this.getCS(session, false)
return this.ik.decryptWithAd(cs, Buffer.alloc(0), ciphertext)
}
public encrypt(plaintext: Buffer, session: NoiseSession): Buffer {
const cs = this.getCS(session);
return this.ik.encryptWithAd(cs, Buffer.alloc(0), plaintext);
public encrypt (plaintext: Buffer, session: NoiseSession): Buffer {
const cs = this.getCS(session)
return this.ik.encryptWithAd(cs, Buffer.alloc(0), plaintext)
}
public getLocalEphemeralKeys(): KeyPair {
public getLocalEphemeralKeys (): KeyPair {
if (!this.session.hs.e) {
throw new Error("Ephemeral keys do not exist.");
throw new Error('Ephemeral keys do not exist.')
}
return this.session.hs.e;
return this.session.hs.e
}
private getCS(session: NoiseSession, encryption = true) {
private getCS (session: NoiseSession, encryption = true) {
if (!session.cs1 || !session.cs2) {
throw new Error("Handshake not completed properly, cipher state does not exist.");
throw new Error('Handshake not completed properly, cipher state does not exist.')
}
if (this.isInitiator) {
return encryption ? session.cs1 : session.cs2;
return encryption ? session.cs1 : session.cs2
} else {
return encryption ? session.cs2 : session.cs1;
return encryption ? session.cs2 : session.cs1
}
}
private setRemoteEarlyData(data: Uint8Array|null|undefined): void {
if(data){
this.remoteEarlyData = Buffer.from(data.buffer, data.byteOffset, data.length);
private setRemoteEarlyData (data: Uint8Array|null|undefined): void {
if (data) {
this.remoteEarlyData = Buffer.from(data.buffer, data.byteOffset, data.length)
}
}
}

View File

@ -1,19 +1,19 @@
import {Buffer} from "buffer";
import {XXHandshake} from "./handshake-xx";
import {XX} from "./handshakes/xx";
import {KeyPair} from "./@types/libp2p";
import {bytes, bytes32} from "./@types/basic";
import {decodePayload, getPeerIdFromPayload, verifySignedPayload} from "./utils";
import {logger, logLocalEphemeralKeys, logRemoteEphemeralKey, logRemoteStaticKey} from "./logger";
import {WrappedConnection} from "./noise";
import {decode0, decode1} from "./encoder";
import PeerId from "peer-id";
import { Buffer } from 'buffer'
import { XXHandshake } from './handshake-xx'
import { XX } from './handshakes/xx'
import { KeyPair } from './@types/libp2p'
import { bytes, bytes32 } from './@types/basic'
import { decodePayload, getPeerIdFromPayload, verifySignedPayload } from './utils'
import { logger, logLocalEphemeralKeys, logRemoteEphemeralKey, logRemoteStaticKey } from './logger'
import { WrappedConnection } from './noise'
import { decode0, decode1 } from './encoder'
import PeerId from 'peer-id'
export class XXFallbackHandshake extends XXHandshake {
private ephemeralKeys?: KeyPair;
private initialMsg: bytes;
constructor(
constructor (
isInitiator: boolean,
payload: bytes,
prologue: bytes32,
@ -22,63 +22,64 @@ export class XXFallbackHandshake extends XXHandshake {
initialMsg: bytes,
remotePeer?: PeerId,
ephemeralKeys?: KeyPair,
handshake?: XX,
handshake?: XX
) {
super(isInitiator, payload, prologue, staticKeypair, connection, remotePeer, handshake);
super(isInitiator, payload, prologue, staticKeypair, connection, remotePeer, handshake)
if (ephemeralKeys) {
this.ephemeralKeys = ephemeralKeys;
this.ephemeralKeys = ephemeralKeys
}
this.initialMsg = initialMsg;
this.initialMsg = initialMsg
}
// stage 0
public async propose(): Promise<void> {
// eslint-disable-next-line require-await
public async propose (): Promise<void> {
if (this.isInitiator) {
this.xx.sendMessage(this.session, Buffer.alloc(0), this.ephemeralKeys);
logger("XX Fallback Stage 0 - Initialized state as the first message was sent by initiator.");
this.xx.sendMessage(this.session, Buffer.alloc(0), this.ephemeralKeys)
logger('XX Fallback Stage 0 - Initialized state as the first message was sent by initiator.')
logLocalEphemeralKeys(this.session.hs.e)
} else {
logger("XX Fallback Stage 0 - Responder decoding initial msg from IK.");
const receivedMessageBuffer = decode0(this.initialMsg);
const {valid} = this.xx.recvMessage(this.session, {
logger('XX Fallback Stage 0 - Responder decoding initial msg from IK.')
const receivedMessageBuffer = decode0(this.initialMsg)
const { valid } = this.xx.recvMessage(this.session, {
ne: receivedMessageBuffer.ne,
ns: Buffer.alloc(0),
ciphertext: Buffer.alloc(0),
});
if(!valid) {
throw new Error("xx fallback stage 0 decryption validation fail");
ciphertext: Buffer.alloc(0)
})
if (!valid) {
throw new Error('xx fallback stage 0 decryption validation fail')
}
logger("XX Fallback Stage 0 - Responder used received message from IK.");
logger('XX Fallback Stage 0 - Responder used received message from IK.')
logRemoteEphemeralKey(this.session.hs.re)
}
}
// stage 1
public async exchange(): Promise<void> {
public async exchange (): Promise<void> {
if (this.isInitiator) {
const receivedMessageBuffer = decode1(this.initialMsg);
const {plaintext, valid} = this.xx.recvMessage(this.session, receivedMessageBuffer);
if(!valid) {
throw new Error("xx fallback stage 1 decryption validation fail");
const receivedMessageBuffer = decode1(this.initialMsg)
const { plaintext, valid } = this.xx.recvMessage(this.session, receivedMessageBuffer)
if (!valid) {
throw new Error('xx fallback stage 1 decryption validation fail')
}
logger('XX Fallback Stage 1 - Initiator used received message from IK.');
logger('XX Fallback Stage 1 - Initiator used received message from IK.')
logRemoteEphemeralKey(this.session.hs.re)
logRemoteStaticKey(this.session.hs.rs)
logger("Initiator going to check remote's signature...");
logger("Initiator going to check remote's signature...")
try {
const decodedPayload = await decodePayload(plaintext);
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload);
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer);
const decodedPayload = await decodePayload(plaintext)
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
this.setRemoteEarlyData(decodedPayload.data)
} catch (e) {
throw new Error(`Error occurred while verifying signed payload from responder: ${e.message}`);
throw new Error(`Error occurred while verifying signed payload from responder: ${e.message}`)
}
logger("All good with the signature!");
logger('All good with the signature!')
} else {
logger("XX Fallback Stage 1 - Responder start");
await super.exchange();
logger("XX Fallback Stage 1 - Responder end");
logger('XX Fallback Stage 1 - Responder start')
await super.exchange()
logger('XX Fallback Stage 1 - Responder end')
}
}
}

View File

@ -1,26 +1,26 @@
import { Buffer } from "buffer";
import { Buffer } from 'buffer'
import { XX } from "./handshakes/xx";
import { KeyPair } from "./@types/libp2p";
import { bytes, bytes32 } from "./@types/basic";
import { NoiseSession } from "./@types/handshake";
import {IHandshake} from "./@types/handshake-interface";
import { XX } from './handshakes/xx'
import { KeyPair } from './@types/libp2p'
import { bytes, bytes32 } from './@types/basic'
import { NoiseSession } from './@types/handshake'
import { IHandshake } from './@types/handshake-interface'
import {
decodePayload,
getPeerIdFromPayload,
verifySignedPayload,
} from "./utils";
verifySignedPayload
} from './utils'
import {
logger,
logLocalStaticKeys,
logLocalEphemeralKeys,
logRemoteEphemeralKey,
logRemoteStaticKey,
logCipherState,
} from "./logger";
import {decode0, decode1, decode2, encode0, encode1, encode2} from "./encoder";
import { WrappedConnection } from "./noise";
import PeerId from "peer-id";
logger,
logLocalStaticKeys,
logLocalEphemeralKeys,
logRemoteEphemeralKey,
logRemoteStaticKey,
logCipherState
} from './logger'
import { decode0, decode1, decode2, encode0, encode1, encode2 } from './encoder'
import { WrappedConnection } from './noise'
import PeerId from 'peer-id'
export class XXHandshake implements IHandshake {
public isInitiator: boolean;
@ -35,139 +35,139 @@ export class XXHandshake implements IHandshake {
private prologue: bytes32;
constructor(
constructor (
isInitiator: boolean,
payload: bytes,
prologue: bytes32,
staticKeypair: KeyPair,
connection: WrappedConnection,
remotePeer?: PeerId,
handshake?: XX,
handshake?: XX
) {
this.isInitiator = isInitiator;
this.payload = payload;
this.prologue = prologue;
this.staticKeypair = staticKeypair;
this.connection = connection;
if(remotePeer) {
this.remotePeer = remotePeer;
this.isInitiator = isInitiator
this.payload = payload
this.prologue = prologue
this.staticKeypair = staticKeypair
this.connection = connection
if (remotePeer) {
this.remotePeer = remotePeer
}
this.xx = handshake || new XX();
this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeypair);
this.xx = handshake || new XX()
this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeypair)
this.remoteEarlyData = Buffer.alloc(0)
}
// stage 0
public async propose(): Promise<void> {
public async propose (): Promise<void> {
logLocalStaticKeys(this.session.hs.s)
if (this.isInitiator) {
logger("Stage 0 - Initiator starting to send first message.");
const messageBuffer = this.xx.sendMessage(this.session, Buffer.alloc(0));
this.connection.writeLP(encode0(messageBuffer));
logger("Stage 0 - Initiator finished sending first message.");
logger('Stage 0 - Initiator starting to send first message.')
const messageBuffer = this.xx.sendMessage(this.session, Buffer.alloc(0))
this.connection.writeLP(encode0(messageBuffer))
logger('Stage 0 - Initiator finished sending first message.')
logLocalEphemeralKeys(this.session.hs.e)
} else {
logger("Stage 0 - Responder waiting to receive first message...");
const receivedMessageBuffer = decode0((await this.connection.readLP()).slice());
const {valid} = this.xx.recvMessage(this.session, receivedMessageBuffer);
if(!valid) {
throw new Error("xx handshake stage 0 validation fail");
logger('Stage 0 - Responder waiting to receive first message...')
const receivedMessageBuffer = decode0((await this.connection.readLP()).slice())
const { valid } = this.xx.recvMessage(this.session, receivedMessageBuffer)
if (!valid) {
throw new Error('xx handshake stage 0 validation fail')
}
logger("Stage 0 - Responder received first message.");
logger('Stage 0 - Responder received first message.')
logRemoteEphemeralKey(this.session.hs.re)
}
}
// stage 1
public async exchange(): Promise<void> {
public async exchange (): Promise<void> {
if (this.isInitiator) {
logger('Stage 1 - Initiator waiting to receive first message from responder...');
const receivedMessageBuffer = decode1((await this.connection.readLP()).slice());
const {plaintext, valid} = this.xx.recvMessage(this.session, receivedMessageBuffer);
if(!valid) {
throw new Error("xx handshake stage 1 validation fail");
logger('Stage 1 - Initiator waiting to receive first message from responder...')
const receivedMessageBuffer = decode1((await this.connection.readLP()).slice())
const { plaintext, valid } = this.xx.recvMessage(this.session, receivedMessageBuffer)
if (!valid) {
throw new Error('xx handshake stage 1 validation fail')
}
logger('Stage 1 - Initiator received the message.');
logger('Stage 1 - Initiator received the message.')
logRemoteEphemeralKey(this.session.hs.re)
logRemoteStaticKey(this.session.hs.rs)
logger("Initiator going to check remote's signature...");
logger("Initiator going to check remote's signature...")
try {
const decodedPayload = await decodePayload(plaintext);
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload);
this.remotePeer = await verifySignedPayload(receivedMessageBuffer.ns, decodedPayload, this.remotePeer);
const decodedPayload = await decodePayload(plaintext)
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
this.remotePeer = await verifySignedPayload(receivedMessageBuffer.ns, decodedPayload, this.remotePeer)
this.setRemoteEarlyData(decodedPayload.data)
} catch (e) {
throw new Error(`Error occurred while verifying signed payload: ${e.message}`);
throw new Error(`Error occurred while verifying signed payload: ${e.message}`)
}
logger("All good with the signature!");
logger('All good with the signature!')
} else {
logger('Stage 1 - Responder sending out first message with signed payload and static key.');
const messageBuffer = this.xx.sendMessage(this.session, this.payload);
this.connection.writeLP(encode1(messageBuffer));
logger('Stage 1 - Responder sending out first message with signed payload and static key.')
const messageBuffer = this.xx.sendMessage(this.session, this.payload)
this.connection.writeLP(encode1(messageBuffer))
logger('Stage 1 - Responder sent the second handshake message with signed payload.')
logLocalEphemeralKeys(this.session.hs.e)
}
}
// stage 2
public async finish(): Promise<void> {
public async finish (): Promise<void> {
if (this.isInitiator) {
logger('Stage 2 - Initiator sending third handshake message.');
const messageBuffer = this.xx.sendMessage(this.session, this.payload);
this.connection.writeLP(encode2(messageBuffer));
logger('Stage 2 - Initiator sent message with signed payload.');
logger('Stage 2 - Initiator sending third handshake message.')
const messageBuffer = this.xx.sendMessage(this.session, this.payload)
this.connection.writeLP(encode2(messageBuffer))
logger('Stage 2 - Initiator sent message with signed payload.')
} else {
logger('Stage 2 - Responder waiting for third handshake message...');
const receivedMessageBuffer = decode2((await this.connection.readLP()).slice());
const {plaintext, valid} = this.xx.recvMessage(this.session, receivedMessageBuffer);
if(!valid) {
throw new Error("xx handshake stage 2 validation fail");
logger('Stage 2 - Responder waiting for third handshake message...')
const receivedMessageBuffer = decode2((await this.connection.readLP()).slice())
const { plaintext, valid } = this.xx.recvMessage(this.session, receivedMessageBuffer)
if (!valid) {
throw new Error('xx handshake stage 2 validation fail')
}
logger('Stage 2 - Responder received the message, finished handshake.');
logger('Stage 2 - Responder received the message, finished handshake.')
try {
const decodedPayload = await decodePayload(plaintext);
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload);
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer);
const decodedPayload = await decodePayload(plaintext)
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
this.setRemoteEarlyData(decodedPayload.data)
} catch (e) {
throw new Error(`Error occurred while verifying signed payload: ${e.message}`);
throw new Error(`Error occurred while verifying signed payload: ${e.message}`)
}
}
logCipherState(this.session)
}
public encrypt(plaintext: bytes, session: NoiseSession): bytes {
const cs = this.getCS(session);
public encrypt (plaintext: bytes, session: NoiseSession): bytes {
const cs = this.getCS(session)
return this.xx.encryptWithAd(cs, Buffer.alloc(0), plaintext);
return this.xx.encryptWithAd(cs, Buffer.alloc(0), plaintext)
}
public decrypt(ciphertext: bytes, session: NoiseSession): {plaintext: bytes; valid: boolean} {
const cs = this.getCS(session, false);
return this.xx.decryptWithAd(cs, Buffer.alloc(0), ciphertext);
public decrypt (ciphertext: bytes, session: NoiseSession): {plaintext: bytes; valid: boolean} {
const cs = this.getCS(session, false)
return this.xx.decryptWithAd(cs, Buffer.alloc(0), ciphertext)
}
public getRemoteStaticKey(): bytes {
return this.session.hs.rs;
public getRemoteStaticKey (): bytes {
return this.session.hs.rs
}
private getCS(session: NoiseSession, encryption = true) {
private getCS (session: NoiseSession, encryption = true) {
if (!session.cs1 || !session.cs2) {
throw new Error("Handshake not completed properly, cipher state does not exist.");
throw new Error('Handshake not completed properly, cipher state does not exist.')
}
if (this.isInitiator) {
return encryption ? session.cs1 : session.cs2;
return encryption ? session.cs1 : session.cs2
} else {
return encryption ? session.cs2 : session.cs1;
return encryption ? session.cs2 : session.cs1
}
}
protected setRemoteEarlyData(data: Uint8Array|null|undefined): void {
if(data){
this.remoteEarlyData = Buffer.from(data.buffer, data.byteOffset, data.length);
protected setRemoteEarlyData (data: Uint8Array|null|undefined): void {
if (data) {
this.remoteEarlyData = Buffer.from(data.buffer, data.byteOffset, data.length)
}
}
}

View File

@ -1,180 +1,179 @@
import {Buffer} from "buffer";
import AEAD from 'bcrypto/lib/js/aead';
import x25519 from 'bcrypto/lib/js/x25519';
import SHA256 from 'bcrypto/lib/js/sha256';
import { Buffer } from 'buffer'
import AEAD from 'bcrypto/lib/js/aead'
import x25519 from 'bcrypto/lib/js/x25519'
import SHA256 from 'bcrypto/lib/js/sha256'
import {bytes, bytes32, uint32} from "../@types/basic";
import {CipherState, MessageBuffer, SymmetricState} from "../@types/handshake";
import {getHkdf} from "../utils";
import {logger} from "../logger";
import { bytes, bytes32, uint32 } from '../@types/basic'
import { CipherState, MessageBuffer, SymmetricState } from '../@types/handshake'
import { getHkdf } from '../utils'
import { logger } from '../logger'
export const MIN_NONCE = 0;
export const MIN_NONCE = 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));
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;
return e
}
public decryptWithAd(cs: CipherState, ad: bytes, ciphertext: bytes): {plaintext: bytes; valid: boolean} {
const {plaintext, valid} = this.decrypt(cs.k, cs.n, ad, ciphertext);
this.setNonce(cs, this.incrementNonce(cs.n));
public decryptWithAd (cs: CipherState, ad: bytes, ciphertext: bytes): {plaintext: bytes; valid: boolean} {
const { plaintext, valid } = this.decrypt(cs.k, cs.n, ad, ciphertext)
this.setNonce(cs, this.incrementNonce(cs.n))
return {plaintext, valid};
return { plaintext, valid }
}
// Cipher state related
protected hasKey(cs: CipherState): boolean {
return !this.isEmptyKey(cs.k);
protected hasKey (cs: CipherState): boolean {
return !this.isEmptyKey(cs.k)
}
protected setNonce(cs: CipherState, nonce: uint32): void {
cs.n = nonce;
protected setNonce (cs: CipherState, nonce: uint32): void {
cs.n = nonce
}
protected createEmptyKey(): bytes32 {
return Buffer.alloc(32);
protected createEmptyKey (): bytes32 {
return Buffer.alloc(32)
}
protected isEmptyKey(k: bytes32): boolean {
const emptyKey = this.createEmptyKey();
return emptyKey.equals(k);
protected isEmptyKey (k: bytes32): boolean {
const emptyKey = this.createEmptyKey()
return emptyKey.equals(k)
}
protected incrementNonce(n: uint32): uint32 {
return n + 1;
protected incrementNonce (n: uint32): uint32 {
return n + 1
}
protected nonceToBytes(n: uint32): bytes {
const nonce = Buffer.alloc(12);
nonce.writeUInt32LE(n, 4);
protected nonceToBytes (n: uint32): bytes {
const nonce = Buffer.alloc(12)
nonce.writeUInt32LE(n, 4)
return nonce;
return nonce
}
protected encrypt(k: bytes32, n: uint32, ad: bytes, plaintext: bytes): bytes {
const nonce = this.nonceToBytes(n);
const ctx = new AEAD();
plaintext = Buffer.from(plaintext);
ctx.init(k, nonce);
ctx.aad(ad);
ctx.encrypt(plaintext);
protected encrypt (k: bytes32, n: uint32, ad: bytes, plaintext: bytes): bytes {
const nonce = this.nonceToBytes(n)
const ctx = new AEAD()
plaintext = Buffer.from(plaintext)
ctx.init(k, nonce)
ctx.aad(ad)
ctx.encrypt(plaintext)
// Encryption is done on the sent reference
return Buffer.concat([plaintext, ctx.final()]);
return Buffer.concat([plaintext, ctx.final()])
}
protected encryptAndHash(ss: SymmetricState, plaintext: bytes): bytes {
let ciphertext;
protected encryptAndHash (ss: SymmetricState, plaintext: bytes): bytes {
let ciphertext
if (this.hasKey(ss.cs)) {
ciphertext = this.encryptWithAd(ss.cs, ss.h, plaintext);
ciphertext = this.encryptWithAd(ss.cs, ss.h, plaintext)
} else {
ciphertext = plaintext;
ciphertext = plaintext
}
this.mixHash(ss, ciphertext);
return ciphertext;
this.mixHash(ss, ciphertext)
return ciphertext
}
protected decrypt(k: bytes32, n: uint32, ad: bytes, ciphertext: bytes): {plaintext: bytes; valid: boolean} {
const nonce = this.nonceToBytes(n);
const ctx = new AEAD();
ciphertext = Buffer.from(ciphertext);
const tag = ciphertext.slice(ciphertext.length - 16);
ciphertext = ciphertext.slice(0, ciphertext.length - 16);
ctx.init(k, nonce);
ctx.aad(ad);
ctx.decrypt(ciphertext);
protected decrypt (k: bytes32, n: uint32, ad: bytes, ciphertext: bytes): {plaintext: bytes; valid: boolean} {
const nonce = this.nonceToBytes(n)
const ctx = new AEAD()
ciphertext = Buffer.from(ciphertext)
const tag = ciphertext.slice(ciphertext.length - 16)
ciphertext = ciphertext.slice(0, ciphertext.length - 16)
ctx.init(k, nonce)
ctx.aad(ad)
ctx.decrypt(ciphertext)
// Decryption is done on the sent reference
return {plaintext: ciphertext, valid: ctx.verify(tag)};
return { plaintext: ciphertext, valid: ctx.verify(tag) }
}
protected decryptAndHash(ss: SymmetricState, ciphertext: bytes): {plaintext: bytes; valid: boolean} {
let plaintext: bytes, valid = true;
protected decryptAndHash (ss: SymmetricState, ciphertext: bytes): {plaintext: bytes; valid: boolean} {
let plaintext: bytes; let valid = true
if (this.hasKey(ss.cs)) {
({plaintext, valid} = this.decryptWithAd(ss.cs, ss.h, ciphertext));
({ plaintext, valid } = this.decryptWithAd(ss.cs, ss.h, ciphertext))
} else {
plaintext = ciphertext;
plaintext = ciphertext
}
this.mixHash(ss, ciphertext);
return {plaintext, valid};
this.mixHash(ss, ciphertext)
return { plaintext, valid }
}
protected dh(privateKey: bytes32, publicKey: bytes32): bytes32 {
protected dh (privateKey: bytes32, publicKey: bytes32): bytes32 {
try {
const derived = x25519.derive(publicKey, privateKey);
const result = Buffer.alloc(32);
derived.copy(result);
return result;
const derived = x25519.derive(publicKey, privateKey)
const result = Buffer.alloc(32)
derived.copy(result)
return result
} catch (e) {
logger(e.message);
return Buffer.alloc(32);
logger(e.message)
return Buffer.alloc(32)
}
}
protected mixHash(ss: SymmetricState, data: bytes): void {
ss.h = this.getHash(ss.h, data);
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 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 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 = MIN_NONCE;
return { k, n };
protected initializeKey (k: bytes32): CipherState {
const n = MIN_NONCE
return { k, n }
}
// Symmetric state related
protected initializeSymmetric(protocolName: string): SymmetricState {
const protocolNameBytes: bytes = Buffer.from(protocolName, 'utf-8');
const h = this.hashProtocolName(protocolNameBytes);
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);
const ck = h
const key = this.createEmptyKey()
const cs: CipherState = this.initializeKey(key)
return { cs, ck, h };
return { cs, ck, h }
}
protected hashProtocolName(protocolName: bytes): bytes32 {
protected hashProtocolName (protocolName: bytes): bytes32 {
if (protocolName.length <= 32) {
const h = Buffer.alloc(32);
protocolName.copy(h);
return h;
const h = Buffer.alloc(32)
protocolName.copy(h)
return h
} else {
return this.getHash(protocolName, Buffer.alloc(0));
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);
protected split (ss: SymmetricState): {cs1: CipherState, cs2: CipherState} {
const [tempk1, tempk2] = getHkdf(ss.ck, Buffer.alloc(0))
const cs1 = this.initializeKey(tempk1)
const cs2 = this.initializeKey(tempk2)
return { cs1, cs2 };
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);
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 };
return { ne, ns, ciphertext }
}
protected readMessageRegular(cs: CipherState, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
return this.decryptWithAd(cs, Buffer.alloc(0), message.ciphertext);
protected readMessageRegular (cs: CipherState, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
return this.decryptWithAd(cs, Buffer.alloc(0), message.ciphertext)
}
}

View File

@ -1,157 +1,156 @@
import {Buffer} from "buffer";
import {CipherState, HandshakeState, MessageBuffer, NoiseSession} from "../@types/handshake";
import {bytes, bytes32} from "../@types/basic";
import {generateKeypair, isValidPublicKey} from "../utils";
import {AbstractHandshake} from "./abstract-handshake";
import {KeyPair} from "../@types/libp2p";
import { Buffer } from 'buffer'
import { CipherState, HandshakeState, MessageBuffer, NoiseSession } from '../@types/handshake'
import { bytes, bytes32 } from '../@types/basic'
import { generateKeypair, isValidPublicKey } from '../utils'
import { AbstractHandshake } from './abstract-handshake'
import { KeyPair } from '../@types/libp2p'
export class IK extends AbstractHandshake {
public initSession(initiator: boolean, prologue: bytes32, s: KeyPair, rs: bytes32): NoiseSession {
const psk = this.createEmptyKey();
public initSession (initiator: boolean, prologue: bytes32, s: KeyPair, rs: bytes32): NoiseSession {
const psk = this.createEmptyKey()
let hs;
let hs
if (initiator) {
hs = this.initializeInitiator(prologue, s, rs, psk);
hs = this.initializeInitiator(prologue, s, rs, psk)
} else {
hs = this.initializeResponder(prologue, s, rs, psk);
hs = this.initializeResponder(prologue, s, rs, psk)
}
return {
hs,
i: initiator,
mc: 0,
};
mc: 0
}
}
public sendMessage(session: NoiseSession, message: bytes): MessageBuffer {
let messageBuffer: MessageBuffer;
public sendMessage (session: NoiseSession, message: bytes): MessageBuffer {
let messageBuffer: MessageBuffer
if (session.mc === 0) {
messageBuffer = this.writeMessageA(session.hs, message);
messageBuffer = this.writeMessageA(session.hs, message)
} else if (session.mc === 1) {
const { messageBuffer: mb, h, cs1, cs2 } = this.writeMessageB(session.hs, message);
messageBuffer = mb;
session.h = h;
session.cs1 = cs1;
session.cs2 = cs2;
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 > 1) {
if (session.i) {
if (!session.cs1) {
throw new Error("CS1 (cipher state) is not defined")
throw new Error('CS1 (cipher state) is not defined')
}
messageBuffer = this.writeMessageRegular(session.cs1, message);
messageBuffer = this.writeMessageRegular(session.cs1, message)
} else {
if (!session.cs2) {
throw new Error("CS2 (cipher state) is not defined")
throw new Error('CS2 (cipher state) is not defined')
}
messageBuffer = this.writeMessageRegular(session.cs2, message);
messageBuffer = this.writeMessageRegular(session.cs2, message)
}
} else {
throw new Error("Session invalid.")
throw new Error('Session invalid.')
}
session.mc++;
return messageBuffer;
session.mc++
return messageBuffer
}
public recvMessage(session: NoiseSession, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
let plaintext = Buffer.alloc(0), valid = false;
public recvMessage (session: NoiseSession, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
let plaintext = Buffer.alloc(0); let valid = false
if (session.mc === 0) {
({plaintext, valid} = this.readMessageA(session.hs, message));
({ plaintext, valid } = this.readMessageA(session.hs, message))
}
if (session.mc === 1) {
const { plaintext: pt, valid: v, h, cs1, cs2 } = this.readMessageB(session.hs, message);
plaintext = pt;
valid = v;
session.h = h;
session.cs1 = cs1;
session.cs2 = cs2;
const { plaintext: pt, valid: v, h, cs1, cs2 } = this.readMessageB(session.hs, message)
plaintext = pt
valid = v
session.h = h
session.cs1 = cs1
session.cs2 = cs2
}
session.mc++;
return {plaintext, valid};
session.mc++
return { plaintext, valid }
}
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);
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.rs));
const ciphertext = this.encryptAndHash(hs.ss, payload);
this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.rs))
const ciphertext = this.encryptAndHash(hs.ss, payload)
return { ne, ns, ciphertext };
return { ne, ns, ciphertext }
}
private writeMessageB(hs: HandshakeState, payload: bytes) {
hs.e = generateKeypair();
const ne = hs.e.publicKey;
this.mixHash(hs.ss, ne);
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);
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): {plaintext: bytes; valid: boolean} {
private readMessageA (hs: HandshakeState, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
if (isValidPublicKey(message.ne)) {
hs.re = message.ne;
hs.re = message.ne
}
this.mixHash(hs.ss, hs.re);
this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.re));
const {plaintext: ns, valid: valid1} = this.decryptAndHash(hs.ss, message.ns);
this.mixHash(hs.ss, hs.re)
this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.re))
const { plaintext: ns, valid: valid1 } = this.decryptAndHash(hs.ss, message.ns)
if (valid1 && ns.length === 32 && isValidPublicKey(ns)) {
hs.rs = ns;
hs.rs = ns
}
this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.rs));
const {plaintext, valid: valid2} = this.decryptAndHash(hs.ss, message.ciphertext);
return {plaintext, valid: (valid1 && valid2)};
this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.rs))
const { plaintext, valid: valid2 } = this.decryptAndHash(hs.ss, message.ciphertext)
return { plaintext, valid: (valid1 && valid2) }
}
private readMessageB(hs: HandshakeState, message: MessageBuffer): {h: bytes; plaintext: bytes; valid: boolean; cs1: CipherState; cs2: CipherState} {
private readMessageB (hs: HandshakeState, message: MessageBuffer): {h: bytes; plaintext: bytes; valid: boolean; cs1: CipherState; cs2: CipherState} {
if (isValidPublicKey(message.ne)) {
hs.re = message.ne;
hs.re = message.ne
}
this.mixHash(hs.ss, hs.re);
this.mixHash(hs.ss, hs.re)
if (!hs.e) {
throw new Error("Handshake state should contain ephemeral key by now.");
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, valid} = this.decryptAndHash(hs.ss, message.ciphertext);
const { cs1, cs2 } = this.split(hs.ss);
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.re))
this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.re))
const { plaintext, valid } = this.decryptAndHash(hs.ss, message.ciphertext)
const { cs1, cs2 } = this.split(hs.ss)
return { h: hs.ss.h, valid, plaintext, cs1, cs2 };
return { h: hs.ss.h, valid, 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);
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 };
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);
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 };
return { ss, s, rs, re, psk }
}
}

View File

@ -1,186 +1,185 @@
import { Buffer } from 'buffer';
import { Buffer } from 'buffer'
import { bytes32, bytes } from '../@types/basic'
import { KeyPair } from '../@types/libp2p'
import {generateKeypair, isValidPublicKey} from '../utils';
import {CipherState, HandshakeState, MessageBuffer, NoiseSession} from "../@types/handshake";
import {AbstractHandshake} from "./abstract-handshake";
import { generateKeypair, isValidPublicKey } from '../utils'
import { CipherState, HandshakeState, MessageBuffer, NoiseSession } from '../@types/handshake'
import { AbstractHandshake } from './abstract-handshake'
export class XX extends AbstractHandshake {
private initializeInitiator(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): HandshakeState {
const name = "Noise_XX_25519_ChaChaPoly_SHA256";
const ss = this.initializeSymmetric(name);
this.mixHash(ss, prologue);
const re = 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)
this.mixHash(ss, prologue)
const re = Buffer.alloc(32)
return { ss, s, rs, psk, re };
return { ss, s, rs, psk, re }
}
private initializeResponder(prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): HandshakeState {
const name = "Noise_XX_25519_ChaChaPoly_SHA256";
const ss = this.initializeSymmetric(name);
this.mixHash(ss, prologue);
const re = Buffer.alloc(32);
private initializeResponder (prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): HandshakeState {
const name = 'Noise_XX_25519_ChaChaPoly_SHA256'
const ss = this.initializeSymmetric(name)
this.mixHash(ss, prologue)
const re = Buffer.alloc(32)
return { ss, s, rs, psk, re };
return { ss, s, rs, psk, re }
}
private writeMessageA(hs: HandshakeState, payload: bytes, e?: KeyPair): MessageBuffer {
const ns = Buffer.alloc(0);
private writeMessageA (hs: HandshakeState, payload: bytes, e?: KeyPair): MessageBuffer {
const ns = Buffer.alloc(0)
if (e) {
hs.e = e;
hs.e = e
} else {
hs.e = generateKeypair();
hs.e = generateKeypair()
}
const ne = hs.e.publicKey;
const ne = hs.e.publicKey
this.mixHash(hs.ss, ne);
const ciphertext = this.encryptAndHash(hs.ss, payload);
this.mixHash(hs.ss, ne)
const ciphertext = this.encryptAndHash(hs.ss, payload)
return {ne, ns, ciphertext};
return { ne, ns, ciphertext }
}
private writeMessageB(hs: HandshakeState, payload: bytes): MessageBuffer {
hs.e = generateKeypair();
const ne = hs.e.publicKey;
this.mixHash(hs.ss, ne);
private writeMessageB (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.re));
const spk = Buffer.from(hs.s.publicKey);
const ns = this.encryptAndHash(hs.ss, spk);
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.re))
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);
this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.re))
const ciphertext = this.encryptAndHash(hs.ss, payload)
return { ne, ns, ciphertext };
return { ne, ns, ciphertext }
}
private writeMessageC(hs: HandshakeState, payload: bytes) {
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);
const ne = this.createEmptyKey();
const messageBuffer: MessageBuffer = {ne, ns, ciphertext};
const { cs1, cs2 } = this.split(hs.ss);
private writeMessageC (hs: HandshakeState, payload: bytes) {
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)
const ne = this.createEmptyKey()
const messageBuffer: MessageBuffer = { ne, ns, ciphertext }
const { cs1, cs2 } = this.split(hs.ss)
return { h: hs.ss.h, messageBuffer, cs1, cs2 };
return { h: hs.ss.h, messageBuffer, cs1, cs2 }
}
private readMessageA(hs: HandshakeState, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
private readMessageA (hs: HandshakeState, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
if (isValidPublicKey(message.ne)) {
hs.re = message.ne;
hs.re = message.ne
}
this.mixHash(hs.ss, hs.re);
return this.decryptAndHash(hs.ss, message.ciphertext);
this.mixHash(hs.ss, hs.re)
return this.decryptAndHash(hs.ss, message.ciphertext)
}
private readMessageB(hs: HandshakeState, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
private readMessageB (hs: HandshakeState, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
if (isValidPublicKey(message.ne)) {
hs.re = message.ne;
hs.re = message.ne
}
this.mixHash(hs.ss, hs.re);
this.mixHash(hs.ss, hs.re)
if (!hs.e) {
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));
const {plaintext: ns, valid: valid1} = this.decryptAndHash(hs.ss, message.ns);
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.re))
const { plaintext: ns, valid: valid1 } = this.decryptAndHash(hs.ss, message.ns)
if (valid1 && ns.length === 32 && isValidPublicKey(ns)) {
hs.rs = ns;
hs.rs = ns
}
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs));
const {plaintext, valid: valid2} = this.decryptAndHash(hs.ss, message.ciphertext);
return {plaintext, valid: (valid1 && valid2)};
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs))
const { plaintext, valid: valid2 } = this.decryptAndHash(hs.ss, message.ciphertext)
return { plaintext, valid: (valid1 && valid2) }
}
private readMessageC(hs: HandshakeState, message: MessageBuffer): {h: bytes; plaintext: bytes; valid: boolean; cs1: CipherState; cs2: CipherState} {
const {plaintext: ns, valid: valid1} = this.decryptAndHash(hs.ss, message.ns);
private readMessageC (hs: HandshakeState, message: MessageBuffer): {h: bytes; plaintext: bytes; valid: boolean; cs1: CipherState; cs2: CipherState} {
const { plaintext: ns, valid: valid1 } = this.decryptAndHash(hs.ss, message.ns)
if (valid1 && ns.length === 32 && isValidPublicKey(ns)) {
hs.rs = ns;
hs.rs = ns
}
if (!hs.e) {
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.rs));
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs))
const {plaintext, valid: valid2} = this.decryptAndHash(hs.ss, message.ciphertext);
const { cs1, cs2 } = this.split(hs.ss);
const { plaintext, valid: valid2 } = this.decryptAndHash(hs.ss, message.ciphertext)
const { cs1, cs2 } = this.split(hs.ss)
return { h: hs.ss.h, plaintext, valid: (valid1 && valid2), cs1, cs2 };
return { h: hs.ss.h, plaintext, valid: (valid1 && valid2), cs1, cs2 }
}
public initSession(initiator: boolean, prologue: bytes32, s: KeyPair): NoiseSession {
const psk = this.createEmptyKey();
const rs = Buffer.alloc(32); // no static key yet
let hs;
public initSession (initiator: boolean, prologue: bytes32, s: KeyPair): NoiseSession {
const psk = this.createEmptyKey()
const rs = Buffer.alloc(32) // no static key yet
let hs
if (initiator) {
hs = this.initializeInitiator(prologue, s, rs, psk);
hs = this.initializeInitiator(prologue, s, rs, psk)
} else {
hs = this.initializeResponder(prologue, s, rs, psk);
hs = this.initializeResponder(prologue, s, rs, psk)
}
return {
hs,
i: initiator,
mc: 0,
};
mc: 0
}
}
public sendMessage(session: NoiseSession, message: bytes, ephemeral?: KeyPair): MessageBuffer {
let messageBuffer: MessageBuffer;
public sendMessage (session: NoiseSession, message: bytes, ephemeral?: KeyPair): MessageBuffer {
let messageBuffer: MessageBuffer
if (session.mc === 0) {
messageBuffer = this.writeMessageA(session.hs, message, ephemeral);
messageBuffer = this.writeMessageA(session.hs, message, ephemeral)
} else if (session.mc === 1) {
messageBuffer = this.writeMessageB(session.hs, message);
messageBuffer = this.writeMessageB(session.hs, message)
} else if (session.mc === 2) {
const { h, messageBuffer: resultingBuffer, cs1, cs2 } = this.writeMessageC(session.hs, message);
messageBuffer = resultingBuffer;
session.h = h;
session.cs1 = cs1;
session.cs2 = cs2;
const { h, messageBuffer: resultingBuffer, cs1, cs2 } = this.writeMessageC(session.hs, message)
messageBuffer = resultingBuffer
session.h = h
session.cs1 = cs1
session.cs2 = cs2
} else if (session.mc > 2) {
if (session.i) {
if (!session.cs1) {
throw new Error("CS1 (cipher state) is not defined")
throw new Error('CS1 (cipher state) is not defined')
}
messageBuffer = this.writeMessageRegular(session.cs1, message);
messageBuffer = this.writeMessageRegular(session.cs1, message)
} else {
if (!session.cs2) {
throw new Error("CS2 (cipher state) is not defined")
throw new Error('CS2 (cipher state) is not defined')
}
messageBuffer = this.writeMessageRegular(session.cs2, message);
messageBuffer = this.writeMessageRegular(session.cs2, message)
}
} else {
throw new Error("Session invalid.")
throw new Error('Session invalid.')
}
session.mc++;
return messageBuffer;
session.mc++
return messageBuffer
}
public recvMessage(session: NoiseSession, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
let plaintext: bytes = Buffer.alloc(0);
let valid = false;
public recvMessage (session: NoiseSession, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
let plaintext: bytes = Buffer.alloc(0)
let valid = false
if (session.mc === 0) {
({plaintext, valid} = this.readMessageA(session.hs, message));
({ plaintext, valid } = this.readMessageA(session.hs, message))
} else if (session.mc === 1) {
({plaintext, valid} = this.readMessageB(session.hs, message));
({ plaintext, valid } = this.readMessageB(session.hs, message))
} else if (session.mc === 2) {
const { h, plaintext: resultingPlaintext, valid: resultingValid, cs1, cs2 } = this.readMessageC(session.hs, message);
plaintext = resultingPlaintext;
valid = resultingValid;
session.h = h;
session.cs1 = cs1;
session.cs2 = cs2;
const { h, plaintext: resultingPlaintext, valid: resultingValid, cs1, cs2 } = this.readMessageC(session.hs, message)
plaintext = resultingPlaintext
valid = resultingValid
session.h = h
session.cs1 = cs1
session.cs2 = cs2
}
session.mc++;
return {plaintext, valid};
session.mc++
return { plaintext, valid }
}
}

View File

@ -1,7 +1,7 @@
import {Noise} from "./noise";
export * from "./noise";
import { Noise } from './noise'
export * from './noise'
/**
* Default configuration, it will generate new noise static key and enable noise pipes (IK handshake).
*/
export const NOISE = new Noise();
export const NOISE = new Noise()

View File

@ -1,5 +1,5 @@
import {bytes, bytes32} from "./@types/basic";
import PeerId from "peer-id";
import { bytes, bytes32 } from './@types/basic'
import PeerId from 'peer-id'
/**
* Storage for static keys of previously connected peers.
@ -7,24 +7,23 @@ import PeerId from "peer-id";
class Keycache {
private storage = new Map<bytes, bytes32>();
public store(peerId: PeerId, key: bytes32): void {
this.storage.set(peerId.id, key);
public store (peerId: PeerId, key: bytes32): void {
this.storage.set(peerId.id, key)
}
public load(peerId?: PeerId): bytes32 | null {
if(!peerId) {
return null;
public load (peerId?: PeerId): bytes32 | null {
if (!peerId) {
return null
}
return this.storage.get(peerId.id) || null;
return this.storage.get(peerId.id) || null
}
public resetStorage(): void {
this.storage.clear();
public resetStorage (): void {
this.storage.clear()
}
}
const KeyCache = new Keycache();
const KeyCache = new Keycache()
export {
KeyCache,
KeyCache
}

View File

@ -1,47 +1,44 @@
import debug from "debug";
import {DUMP_SESSION_KEYS} from './constants';
import { KeyPair } from "./@types/libp2p";
import { NoiseSession, SymmetricState } from "./@types/handshake";
import debug from 'debug'
import { DUMP_SESSION_KEYS } from './constants'
import { KeyPair } from './@types/libp2p'
import { NoiseSession } from './@types/handshake'
export const logger = debug('libp2p:noise');
export const logger = debug('libp2p:noise')
let keyLogger;
if(DUMP_SESSION_KEYS){
let keyLogger
if (DUMP_SESSION_KEYS) {
keyLogger = logger
}
else {
} else {
keyLogger = () => { /* do nothing */ }
}
export function logLocalStaticKeys(s: KeyPair): void {
export function logLocalStaticKeys (s: KeyPair): void {
keyLogger(`LOCAL_STATIC_PUBLIC_KEY ${s.publicKey.toString('hex')}`)
keyLogger(`LOCAL_STATIC_PRIVATE_KEY ${s.privateKey.toString('hex')}`)
}
export function logLocalEphemeralKeys(e: KeyPair|undefined): void {
if(e){
export function logLocalEphemeralKeys (e: KeyPair|undefined): void {
if (e) {
keyLogger(`LOCAL_PUBLIC_EPHEMERAL_KEY ${e.publicKey.toString('hex')}`)
keyLogger(`LOCAL_PRIVATE_EPHEMERAL_KEY ${e.privateKey.toString('hex')}`)
}
else{
} else {
keyLogger('Missing local ephemeral keys.')
}
}
export function logRemoteStaticKey(rs: Buffer): void {
export function logRemoteStaticKey (rs: Buffer): void {
keyLogger(`REMOTE_STATIC_PUBLIC_KEY ${rs.toString('hex')}`)
}
export function logRemoteEphemeralKey(re: Buffer): void {
export function logRemoteEphemeralKey (re: Buffer): void {
keyLogger(`REMOTE_EPHEMERAL_PUBLIC_KEY ${re.toString('hex')}`)
}
export function logCipherState(session: NoiseSession): void {
if(session.cs1 && session.cs2){
export function logCipherState (session: NoiseSession): void {
if (session.cs1 && session.cs2) {
keyLogger(`CIPHER_STATE_1 ${session.cs1.n} ${session.cs1.k.toString('hex')}`)
keyLogger(`CIPHER_STATE_2 ${session.cs2.n} ${session.cs2.k.toString('hex')}`)
}
else{
} else {
keyLogger('Missing cipher state.')
}
}

View File

@ -1,25 +1,25 @@
import x25519 from 'bcrypto/lib/js/x25519';
import {Buffer} from "buffer";
import Wrap from 'it-pb-rpc';
import DuplexPair from 'it-pair/duplex';
import ensureBuffer from 'it-buffer';
import pipe from 'it-pipe';
import {encode, decode} from 'it-length-prefixed';
import x25519 from 'bcrypto/lib/js/x25519'
import { Buffer } from 'buffer'
import Wrap from 'it-pb-rpc'
import DuplexPair from 'it-pair/duplex'
import ensureBuffer from 'it-buffer'
import pipe from 'it-pipe'
import { encode, decode } from 'it-length-prefixed'
import {XXHandshake} from "./handshake-xx";
import {IKHandshake} from "./handshake-ik";
import {XXFallbackHandshake} from "./handshake-xx-fallback";
import {generateKeypair, getPayload} from "./utils";
import {uint16BEDecode, uint16BEEncode} from "./encoder";
import {decryptStream, encryptStream} from "./crypto";
import {bytes} from "./@types/basic";
import {INoiseConnection, KeyPair, SecureOutbound} from "./@types/libp2p";
import {Duplex} from "it-pair";
import {IHandshake} from "./@types/handshake-interface";
import {KeyCache} from "./keycache";
import {logger} from "./logger";
import PeerId from "peer-id";
import {NOISE_MSG_MAX_LENGTH_BYTES} from "./constants";
import { XXHandshake } from './handshake-xx'
import { IKHandshake } from './handshake-ik'
import { XXFallbackHandshake } from './handshake-xx-fallback'
import { generateKeypair, getPayload } from './utils'
import { uint16BEDecode, uint16BEEncode } from './encoder'
import { decryptStream, encryptStream } from './crypto'
import { bytes } from './@types/basic'
import { INoiseConnection, KeyPair, SecureOutbound } from './@types/libp2p'
import { Duplex } from 'it-pair'
import { IHandshake } from './@types/handshake-interface'
import { KeyCache } from './keycache'
import { logger } from './logger'
import PeerId from 'peer-id'
import { NOISE_MSG_MAX_LENGTH_BYTES } from './constants'
export type WrappedConnection = ReturnType<typeof Wrap>;
@ -31,7 +31,7 @@ type HandshakeParams = {
};
export class Noise implements INoiseConnection {
public protocol = "/noise";
public protocol = '/noise';
private readonly prologue = Buffer.alloc(0);
private readonly staticKeys: KeyPair;
@ -40,206 +40,209 @@ export class Noise implements INoiseConnection {
/**
*
* @param staticNoiseKey x25519 private key, reuse for faster handshakes
* @param earlyData
* @param {bytes} staticNoiseKey x25519 private key, reuse for faster handshakes
* @param {bytes} earlyData
*/
constructor(staticNoiseKey?: bytes, earlyData?: bytes) {
this.earlyData = earlyData || Buffer.alloc(0);
//disabled until properly specked
this.useNoisePipes = false;
constructor (staticNoiseKey?: bytes, earlyData?: bytes) {
this.earlyData = earlyData || Buffer.alloc(0)
// disabled until properly specked
this.useNoisePipes = false
if (staticNoiseKey) {
const publicKey = x25519.publicKeyCreate(staticNoiseKey);
const publicKey = x25519.publicKeyCreate(staticNoiseKey)
this.staticKeys = {
privateKey: staticNoiseKey,
publicKey,
publicKey
}
} else {
this.staticKeys = generateKeypair();
this.staticKeys = generateKeypair()
}
}
/**
* Encrypt outgoing data to the remote party (handshake as initiator)
* @param {PeerId} localPeer - PeerId of the receiving peer
* @param connection - streaming iterable duplex that will be encrypted
* @param {any} connection - streaming iterable duplex that will be encrypted
* @param {PeerId} remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer.
* @returns {Promise<SecureOutbound>}
*/
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,
{
// wrong types in repo
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
lengthEncoder: uint16BEEncode,
lengthDecoder: uint16BEDecode,
maxDataLength: NOISE_MSG_MAX_LENGTH_BYTES
}
);
)
const handshake = await this.performHandshake({
connection: wrappedConnection,
isInitiator: true,
localPeer,
remotePeer,
});
const conn = await this.createSecureConnection(wrappedConnection, handshake);
remotePeer
})
const conn = await this.createSecureConnection(wrappedConnection, handshake)
return {
conn,
remoteEarlyData: handshake.remoteEarlyData,
remotePeer: handshake.remotePeer,
remotePeer: handshake.remotePeer
}
}
/**
* Decrypt incoming data (handshake as responder).
* @param {PeerId} localPeer - PeerId of the receiving peer.
* @param connection - streaming iterable duplex that will be encryption.
* @param {any} connection - streaming iterable duplex that will be encryption.
* @param {PeerId} remotePeer - optional PeerId of the initiating peer, if known. This may only exist during transport upgrades.
* @returns {Promise<SecureOutbound>}
*/
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,
{
// wrong types in repo
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
lengthEncoder: uint16BEEncode,
lengthDecoder: uint16BEDecode,
maxDataLength: NOISE_MSG_MAX_LENGTH_BYTES
}
);
)
const handshake = await this.performHandshake({
connection: wrappedConnection,
isInitiator: false,
localPeer,
remotePeer
});
const conn = await this.createSecureConnection(wrappedConnection, handshake);
})
const conn = await this.createSecureConnection(wrappedConnection, handshake)
return {
conn,
remoteEarlyData: handshake.remoteEarlyData,
remotePeer: handshake.remotePeer
};
}
}
/**
* If Noise pipes supported, tries IK handshake first with XX as fallback if it fails.
* If noise pipes disabled or remote peer static key is unknown, use XX.
* @param params
* @param {HandshakeParams} params
*/
private async performHandshake(params: HandshakeParams): Promise<IHandshake> {
const payload = await getPayload(params.localPeer, this.staticKeys.publicKey, this.earlyData);
let tryIK = this.useNoisePipes;
if(params.isInitiator && KeyCache.load(params.remotePeer) === null) {
//if we are initiator and remote static key is unknown, don't try IK
tryIK = false;
private async performHandshake (params: HandshakeParams): Promise<IHandshake> {
const payload = await getPayload(params.localPeer, this.staticKeys.publicKey, this.earlyData)
let tryIK = this.useNoisePipes
if (params.isInitiator && KeyCache.load(params.remotePeer) === null) {
// if we are initiator and remote static key is unknown, don't try IK
tryIK = false
}
// Try IK if acting as responder or initiator that has remote's static key.
if (tryIK) {
// Try IK first
const { remotePeer, connection, isInitiator } = params;
const { remotePeer, connection, isInitiator } = params
const ikHandshake = new IKHandshake(
isInitiator,
payload,
this.prologue,
this.staticKeys,
connection,
//safe to cast as we did checks
// safe to cast as we did checks
KeyCache.load(params.remotePeer) || Buffer.alloc(32),
remotePeer as PeerId,
);
remotePeer as PeerId
)
try {
return await this.performIKHandshake(ikHandshake);
return await this.performIKHandshake(ikHandshake)
} catch (e) {
// IK failed, go to XX fallback
let ephemeralKeys;
let ephemeralKeys
if (params.isInitiator) {
ephemeralKeys = ikHandshake.getLocalEphemeralKeys();
ephemeralKeys = ikHandshake.getLocalEphemeralKeys()
}
return await this.performXXFallbackHandshake(params, payload, e.initialMsg, ephemeralKeys);
return await this.performXXFallbackHandshake(params, payload, e.initialMsg, ephemeralKeys)
}
} else {
// run XX handshake
return await this.performXXHandshake(params, payload);
return await this.performXXHandshake(params, payload)
}
}
private async performXXFallbackHandshake(
private async performXXFallbackHandshake (
params: HandshakeParams,
payload: bytes,
initialMsg: bytes,
ephemeralKeys?: KeyPair,
ephemeralKeys?: KeyPair
): Promise<XXFallbackHandshake> {
const { isInitiator, remotePeer, connection } = params;
const { isInitiator, remotePeer, connection } = params
const handshake =
new XXFallbackHandshake(isInitiator, payload, this.prologue, this.staticKeys, connection, initialMsg, remotePeer, ephemeralKeys);
new XXFallbackHandshake(isInitiator, payload, this.prologue, this.staticKeys, connection, initialMsg, remotePeer, ephemeralKeys)
try {
await handshake.propose();
await handshake.exchange();
await handshake.finish();
await handshake.propose()
await handshake.exchange()
await handshake.finish()
} catch (e) {
logger(e);
throw new Error(`Error occurred during XX Fallback handshake: ${e.message}`);
logger(e)
throw new Error(`Error occurred during XX Fallback handshake: ${e.message}`)
}
return handshake;
return handshake
}
private async performXXHandshake(
private async performXXHandshake (
params: HandshakeParams,
payload: bytes,
payload: bytes
): Promise<XXHandshake> {
const { isInitiator, remotePeer, connection } = params;
const handshake = new XXHandshake(isInitiator, payload, this.prologue, this.staticKeys, connection, remotePeer);
const { isInitiator, remotePeer, connection } = params
const handshake = new XXHandshake(isInitiator, payload, this.prologue, this.staticKeys, connection, remotePeer)
try {
await handshake.propose();
await handshake.exchange();
await handshake.finish();
await handshake.propose()
await handshake.exchange()
await handshake.finish()
if (this.useNoisePipes && handshake.remotePeer) {
KeyCache.store(handshake.remotePeer, handshake.getRemoteStaticKey());
KeyCache.store(handshake.remotePeer, handshake.getRemoteStaticKey())
}
} catch (e) {
throw new Error(`Error occurred during XX handshake: ${e.message}`);
throw new Error(`Error occurred during XX handshake: ${e.message}`)
}
return handshake;
return handshake
}
private async performIKHandshake(
handshake: IKHandshake,
private async performIKHandshake (
handshake: IKHandshake
): Promise<IKHandshake> {
await handshake.stage0()
await handshake.stage1()
await handshake.stage0();
await handshake.stage1();
return handshake;
return handshake
}
private async createSecureConnection(
private async createSecureConnection (
connection: WrappedConnection,
handshake: IHandshake,
handshake: IHandshake
): Promise<Duplex> {
// Create encryption box/unbox wrapper
const [secure, user] = DuplexPair();
const network = connection.unwrap();
const [secure, user] = DuplexPair()
const network = connection.unwrap()
pipe(
await pipe(
secure, // write to wrapper
ensureBuffer, // ensure any type of data is converted to buffer
encryptStream(handshake), // data is encrypted
encode({ lengthEncoder: uint16BEEncode }), // prefix with message length
network, // send to the remote peer
decode({ lengthDecoder: uint16BEDecode}), // read message length prefix
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
);
)
return user;
return user
}
}

View File

@ -1,33 +1,25 @@
/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/
(function(global, factory) { /* global define, require, module */
/* eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars */
(function (global, factory) { /* global define, require, module */
/* AMD */ if (typeof define === 'function' && define.amd) { define(['protobufjs/minimal'], factory) }
/* AMD */ if (typeof define === 'function' && define.amd)
define(["protobufjs/minimal"], factory);
/* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports) { module.exports = factory(require('protobufjs/minimal')) }
})(this, function ($protobuf) {
// Common aliases
var $Reader = $protobuf.Reader; var $Writer = $protobuf.Writer; var $util = $protobuf.util
/* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports)
module.exports = factory(require("protobufjs/minimal"));
// Exported root namespace
var $root = $protobuf.roots.default || ($protobuf.roots.default = {})
})(this, function($protobuf) {
"use strict";
// Common aliases
var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util;
// Exported root namespace
var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {});
$root.pb = (function() {
/**
$root.pb = (function () {
/**
* Namespace pb.
* @exports pb
* @namespace
*/
var pb = {};
pb.NoiseHandshakePayload = (function() {
/**
var pb = {}
pb.NoiseHandshakePayload = (function () {
/**
* Properties of a NoiseHandshakePayload.
* @memberof pb
* @interface INoiseHandshakePayload
@ -35,8 +27,8 @@
* @property {Uint8Array|null} [identitySig] NoiseHandshakePayload identitySig
* @property {Uint8Array|null} [data] NoiseHandshakePayload data
*/
/**
/**
* Constructs a new NoiseHandshakePayload.
* @memberof pb
* @classdesc Represents a NoiseHandshakePayload.
@ -44,38 +36,39 @@
* @constructor
* @param {pb.INoiseHandshakePayload=} [properties] Properties to set
*/
function NoiseHandshakePayload(properties) {
if (properties)
for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i)
if (properties[keys[i]] != null)
this[keys[i]] = properties[keys[i]];
}
/**
function NoiseHandshakePayload (properties) {
if (properties) {
for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) {
if (properties[keys[i]] != null) { this[keys[i]] = properties[keys[i]] }
}
}
}
/**
* NoiseHandshakePayload identityKey.
* @member {Uint8Array} identityKey
* @memberof pb.NoiseHandshakePayload
* @instance
*/
NoiseHandshakePayload.prototype.identityKey = $util.newBuffer([]);
/**
NoiseHandshakePayload.prototype.identityKey = $util.newBuffer([])
/**
* NoiseHandshakePayload identitySig.
* @member {Uint8Array} identitySig
* @memberof pb.NoiseHandshakePayload
* @instance
*/
NoiseHandshakePayload.prototype.identitySig = $util.newBuffer([]);
/**
NoiseHandshakePayload.prototype.identitySig = $util.newBuffer([])
/**
* NoiseHandshakePayload data.
* @member {Uint8Array} data
* @memberof pb.NoiseHandshakePayload
* @instance
*/
NoiseHandshakePayload.prototype.data = $util.newBuffer([]);
/**
NoiseHandshakePayload.prototype.data = $util.newBuffer([])
/**
* Creates a new NoiseHandshakePayload instance using the specified properties.
* @function create
* @memberof pb.NoiseHandshakePayload
@ -83,11 +76,11 @@
* @param {pb.INoiseHandshakePayload=} [properties] Properties to set
* @returns {pb.NoiseHandshakePayload} NoiseHandshakePayload instance
*/
NoiseHandshakePayload.create = function create(properties) {
return new NoiseHandshakePayload(properties);
};
/**
NoiseHandshakePayload.create = function create (properties) {
return new NoiseHandshakePayload(properties)
}
/**
* Encodes the specified NoiseHandshakePayload message. Does not implicitly {@link pb.NoiseHandshakePayload.verify|verify} messages.
* @function encode
* @memberof pb.NoiseHandshakePayload
@ -96,19 +89,15 @@
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
NoiseHandshakePayload.encode = function encode(message, writer) {
if (!writer)
writer = $Writer.create();
if (message.identityKey != null && message.hasOwnProperty("identityKey"))
writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.identityKey);
if (message.identitySig != null && message.hasOwnProperty("identitySig"))
writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.identitySig);
if (message.data != null && message.hasOwnProperty("data"))
writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.data);
return writer;
};
/**
NoiseHandshakePayload.encode = function encode (message, writer) {
if (!writer) { writer = $Writer.create() }
if (message.identityKey != null && message.hasOwnProperty('identityKey')) { writer.uint32(/* id 1, wireType 2 = */10).bytes(message.identityKey) }
if (message.identitySig != null && message.hasOwnProperty('identitySig')) { writer.uint32(/* id 2, wireType 2 = */18).bytes(message.identitySig) }
if (message.data != null && message.hasOwnProperty('data')) { writer.uint32(/* id 3, wireType 2 = */26).bytes(message.data) }
return writer
}
/**
* Encodes the specified NoiseHandshakePayload message, length delimited. Does not implicitly {@link pb.NoiseHandshakePayload.verify|verify} messages.
* @function encodeDelimited
* @memberof pb.NoiseHandshakePayload
@ -117,11 +106,11 @@
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
NoiseHandshakePayload.encodeDelimited = function encodeDelimited(message, writer) {
return this.encode(message, writer).ldelim();
};
/**
NoiseHandshakePayload.encodeDelimited = function encodeDelimited (message, writer) {
return this.encode(message, writer).ldelim()
}
/**
* Decodes a NoiseHandshakePayload message from the specified reader or buffer.
* @function decode
* @memberof pb.NoiseHandshakePayload
@ -132,31 +121,30 @@
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
NoiseHandshakePayload.decode = function decode(reader, length) {
if (!(reader instanceof $Reader))
reader = $Reader.create(reader);
var end = length === undefined ? reader.len : reader.pos + length, message = new $root.pb.NoiseHandshakePayload();
while (reader.pos < end) {
var tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.identityKey = reader.bytes();
break;
case 2:
message.identitySig = reader.bytes();
break;
case 3:
message.data = reader.bytes();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
};
/**
NoiseHandshakePayload.decode = function decode (reader, length) {
if (!(reader instanceof $Reader)) { reader = $Reader.create(reader) }
var end = length === undefined ? reader.len : reader.pos + length; var message = new $root.pb.NoiseHandshakePayload()
while (reader.pos < end) {
var tag = reader.uint32()
switch (tag >>> 3) {
case 1:
message.identityKey = reader.bytes()
break
case 2:
message.identitySig = reader.bytes()
break
case 3:
message.data = reader.bytes()
break
default:
reader.skipType(tag & 7)
break
}
}
return message
}
/**
* Decodes a NoiseHandshakePayload message from the specified reader or buffer, length delimited.
* @function decodeDelimited
* @memberof pb.NoiseHandshakePayload
@ -166,13 +154,12 @@
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
NoiseHandshakePayload.decodeDelimited = function decodeDelimited(reader) {
if (!(reader instanceof $Reader))
reader = new $Reader(reader);
return this.decode(reader, reader.uint32());
};
/**
NoiseHandshakePayload.decodeDelimited = function decodeDelimited (reader) {
if (!(reader instanceof $Reader)) { reader = new $Reader(reader) }
return this.decode(reader, reader.uint32())
}
/**
* Verifies a NoiseHandshakePayload message.
* @function verify
* @memberof pb.NoiseHandshakePayload
@ -180,22 +167,21 @@
* @param {Object.<string,*>} message Plain object to verify
* @returns {string|null} `null` if valid, otherwise the reason why it is not
*/
NoiseHandshakePayload.verify = function verify(message) {
if (typeof message !== "object" || message === null)
return "object expected";
if (message.identityKey != null && message.hasOwnProperty("identityKey"))
if (!(message.identityKey && typeof message.identityKey.length === "number" || $util.isString(message.identityKey)))
return "identityKey: buffer expected";
if (message.identitySig != null && message.hasOwnProperty("identitySig"))
if (!(message.identitySig && typeof message.identitySig.length === "number" || $util.isString(message.identitySig)))
return "identitySig: buffer expected";
if (message.data != null && message.hasOwnProperty("data"))
if (!(message.data && typeof message.data.length === "number" || $util.isString(message.data)))
return "data: buffer expected";
return null;
};
/**
NoiseHandshakePayload.verify = function verify (message) {
if (typeof message !== 'object' || message === null) { return 'object expected' }
if (message.identityKey != null && message.hasOwnProperty('identityKey')) {
if (!(message.identityKey && typeof message.identityKey.length === 'number' || $util.isString(message.identityKey))) { return 'identityKey: buffer expected' }
}
if (message.identitySig != null && message.hasOwnProperty('identitySig')) {
if (!(message.identitySig && typeof message.identitySig.length === 'number' || $util.isString(message.identitySig))) { return 'identitySig: buffer expected' }
}
if (message.data != null && message.hasOwnProperty('data')) {
if (!(message.data && typeof message.data.length === 'number' || $util.isString(message.data))) { return 'data: buffer expected' }
}
return null
}
/**
* Creates a NoiseHandshakePayload message from a plain object. Also converts values to their respective internal types.
* @function fromObject
* @memberof pb.NoiseHandshakePayload
@ -203,29 +189,22 @@
* @param {Object.<string,*>} object Plain object
* @returns {pb.NoiseHandshakePayload} NoiseHandshakePayload
*/
NoiseHandshakePayload.fromObject = function fromObject(object) {
if (object instanceof $root.pb.NoiseHandshakePayload)
return object;
var message = new $root.pb.NoiseHandshakePayload();
if (object.identityKey != null)
if (typeof object.identityKey === "string")
$util.base64.decode(object.identityKey, message.identityKey = $util.newBuffer($util.base64.length(object.identityKey)), 0);
else if (object.identityKey.length)
message.identityKey = object.identityKey;
if (object.identitySig != null)
if (typeof object.identitySig === "string")
$util.base64.decode(object.identitySig, message.identitySig = $util.newBuffer($util.base64.length(object.identitySig)), 0);
else if (object.identitySig.length)
message.identitySig = object.identitySig;
if (object.data != null)
if (typeof object.data === "string")
$util.base64.decode(object.data, message.data = $util.newBuffer($util.base64.length(object.data)), 0);
else if (object.data.length)
message.data = object.data;
return message;
};
/**
NoiseHandshakePayload.fromObject = function fromObject (object) {
if (object instanceof $root.pb.NoiseHandshakePayload) { return object }
var message = new $root.pb.NoiseHandshakePayload()
if (object.identityKey != null) {
if (typeof object.identityKey === 'string') { $util.base64.decode(object.identityKey, message.identityKey = $util.newBuffer($util.base64.length(object.identityKey)), 0) } else if (object.identityKey.length) { message.identityKey = object.identityKey }
}
if (object.identitySig != null) {
if (typeof object.identitySig === 'string') { $util.base64.decode(object.identitySig, message.identitySig = $util.newBuffer($util.base64.length(object.identitySig)), 0) } else if (object.identitySig.length) { message.identitySig = object.identitySig }
}
if (object.data != null) {
if (typeof object.data === 'string') { $util.base64.decode(object.data, message.data = $util.newBuffer($util.base64.length(object.data)), 0) } else if (object.data.length) { message.data = object.data }
}
return message
}
/**
* Creates a plain object from a NoiseHandshakePayload message. Also converts values to other types if specified.
* @function toObject
* @memberof pb.NoiseHandshakePayload
@ -234,58 +213,45 @@
* @param {$protobuf.IConversionOptions} [options] Conversion options
* @returns {Object.<string,*>} Plain object
*/
NoiseHandshakePayload.toObject = function toObject(message, options) {
if (!options)
options = {};
var object = {};
if (options.defaults) {
if (options.bytes === String)
object.identityKey = "";
else {
object.identityKey = [];
if (options.bytes !== Array)
object.identityKey = $util.newBuffer(object.identityKey);
}
if (options.bytes === String)
object.identitySig = "";
else {
object.identitySig = [];
if (options.bytes !== Array)
object.identitySig = $util.newBuffer(object.identitySig);
}
if (options.bytes === String)
object.data = "";
else {
object.data = [];
if (options.bytes !== Array)
object.data = $util.newBuffer(object.data);
}
}
if (message.identityKey != null && message.hasOwnProperty("identityKey"))
object.identityKey = options.bytes === String ? $util.base64.encode(message.identityKey, 0, message.identityKey.length) : options.bytes === Array ? Array.prototype.slice.call(message.identityKey) : message.identityKey;
if (message.identitySig != null && message.hasOwnProperty("identitySig"))
object.identitySig = options.bytes === String ? $util.base64.encode(message.identitySig, 0, message.identitySig.length) : options.bytes === Array ? Array.prototype.slice.call(message.identitySig) : message.identitySig;
if (message.data != null && message.hasOwnProperty("data"))
object.data = options.bytes === String ? $util.base64.encode(message.data, 0, message.data.length) : options.bytes === Array ? Array.prototype.slice.call(message.data) : message.data;
return object;
};
/**
NoiseHandshakePayload.toObject = function toObject (message, options) {
if (!options) { options = {} }
var object = {}
if (options.defaults) {
if (options.bytes === String) { object.identityKey = '' } else {
object.identityKey = []
if (options.bytes !== Array) { object.identityKey = $util.newBuffer(object.identityKey) }
}
if (options.bytes === String) { object.identitySig = '' } else {
object.identitySig = []
if (options.bytes !== Array) { object.identitySig = $util.newBuffer(object.identitySig) }
}
if (options.bytes === String) { object.data = '' } else {
object.data = []
if (options.bytes !== Array) { object.data = $util.newBuffer(object.data) }
}
}
if (message.identityKey != null && message.hasOwnProperty('identityKey')) { object.identityKey = options.bytes === String ? $util.base64.encode(message.identityKey, 0, message.identityKey.length) : options.bytes === Array ? Array.prototype.slice.call(message.identityKey) : message.identityKey }
if (message.identitySig != null && message.hasOwnProperty('identitySig')) { object.identitySig = options.bytes === String ? $util.base64.encode(message.identitySig, 0, message.identitySig.length) : options.bytes === Array ? Array.prototype.slice.call(message.identitySig) : message.identitySig }
if (message.data != null && message.hasOwnProperty('data')) { object.data = options.bytes === String ? $util.base64.encode(message.data, 0, message.data.length) : options.bytes === Array ? Array.prototype.slice.call(message.data) : message.data }
return object
}
/**
* Converts this NoiseHandshakePayload to JSON.
* @function toJSON
* @memberof pb.NoiseHandshakePayload
* @instance
* @returns {Object.<string,*>} JSON object
*/
NoiseHandshakePayload.prototype.toJSON = function toJSON() {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
};
return NoiseHandshakePayload;
})();
return pb;
})();
NoiseHandshakePayload.prototype.toJSON = function toJSON () {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions)
}
return $root;
});
return NoiseHandshakePayload
})()
return pb
})()
return $root
})

View File

@ -1,78 +1,76 @@
import HKDF from 'bcrypto/lib/hkdf';
import x25519 from 'bcrypto/lib/js/x25519';
import SHA256 from 'bcrypto/lib/js/sha256';
import {Buffer} from "buffer";
import PeerId from "peer-id";
import {keys} from 'libp2p-crypto';
import {KeyPair} from "./@types/libp2p";
import {bytes, bytes32} from "./@types/basic";
import {Hkdf, INoisePayload} from "./@types/handshake";
import {pb} from "./proto/payload";
import HKDF from 'bcrypto/lib/hkdf'
import x25519 from 'bcrypto/lib/js/x25519'
import SHA256 from 'bcrypto/lib/js/sha256'
import { Buffer } from 'buffer'
import PeerId from 'peer-id'
import { keys } from 'libp2p-crypto'
import { KeyPair } from './@types/libp2p'
import { bytes, bytes32 } from './@types/basic'
import { Hkdf, INoisePayload } from './@types/handshake'
import { pb } from './proto/payload'
const NoiseHandshakePayloadProto = pb.NoiseHandshakePayload;
const NoiseHandshakePayloadProto = pb.NoiseHandshakePayload
export function generateKeypair(): KeyPair {
const privateKey = x25519.privateKeyGenerate();
const publicKey = x25519.publicKeyCreate(privateKey);
export function generateKeypair (): KeyPair {
const privateKey = x25519.privateKeyGenerate()
const publicKey = x25519.publicKeyCreate(privateKey)
return {
publicKey,
privateKey,
privateKey
}
}
export async function getPayload(
export async function getPayload (
localPeer: PeerId,
staticPublicKey: bytes,
earlyData?: bytes,
earlyData?: bytes
): Promise<bytes> {
const signedPayload = await signPayload(localPeer, getHandshakePayload(staticPublicKey));
const earlyDataPayload = earlyData || Buffer.alloc(0);
const signedPayload = await signPayload(localPeer, getHandshakePayload(staticPublicKey))
const earlyDataPayload = earlyData || Buffer.alloc(0)
return await createHandshakePayload(
localPeer.marshalPubKey(),
signedPayload,
earlyDataPayload
);
)
}
export async function createHandshakePayload(
export function createHandshakePayload (
libp2pPublicKey: bytes,
signedPayload: bytes,
earlyData?: bytes,
): Promise<bytes> {
earlyData?: bytes
): bytes {
const payloadInit = NoiseHandshakePayloadProto.create({
identityKey: libp2pPublicKey,
identitySig: signedPayload,
data: earlyData || null,
});
data: earlyData || null
})
return Buffer.from(NoiseHandshakePayloadProto.encode(payloadInit).finish());
return Buffer.from(NoiseHandshakePayloadProto.encode(payloadInit).finish())
}
export async function signPayload(peerId: PeerId, payload: bytes): Promise<bytes> {
return peerId.privKey.sign(payload);
export async function signPayload (peerId: PeerId, payload: bytes): Promise<bytes> {
return await peerId.privKey.sign(payload)
}
export async function getPeerIdFromPayload(payload: pb.INoiseHandshakePayload): Promise<PeerId> {
return await PeerId.createFromPubKey(Buffer.from(payload.identityKey as Uint8Array));
export async function getPeerIdFromPayload (payload: pb.INoiseHandshakePayload): Promise<PeerId> {
return await PeerId.createFromPubKey(Buffer.from(payload.identityKey as Uint8Array))
}
export async function decodePayload(payload: bytes|Uint8Array): Promise<pb.INoiseHandshakePayload> {
export function decodePayload (payload: bytes|Uint8Array): pb.INoiseHandshakePayload {
return NoiseHandshakePayloadProto.toObject(
NoiseHandshakePayloadProto.decode(Buffer.from(payload))
) as INoisePayload;
) as INoisePayload
}
export function getHandshakePayload(publicKey: bytes): bytes {
return Buffer.concat([Buffer.from("noise-libp2p-static-key:"), publicKey]);
export function getHandshakePayload (publicKey: bytes): bytes {
return Buffer.concat([Buffer.from('noise-libp2p-static-key:'), publicKey])
}
async function isValidPeerId(peerId: bytes, publicKeyProtobuf: bytes) {
const generatedPeerId = await PeerId.createFromPubKey(publicKeyProtobuf);
return generatedPeerId.id.equals(peerId);
async function isValidPeerId (peerId: bytes, publicKeyProtobuf: bytes) {
const generatedPeerId = await PeerId.createFromPubKey(publicKeyProtobuf)
return generatedPeerId.id.equals(peerId)
}
/**
@ -82,36 +80,36 @@ async function isValidPeerId(peerId: bytes, publicKeyProtobuf: bytes) {
* @param {PeerId} remotePeer - owner's libp2p peer ID
* @returns {Promise<PeerId>} - peer ID of payload owner
*/
export async function verifySignedPayload(
export async function verifySignedPayload (
noiseStaticKey: bytes,
payload: pb.INoiseHandshakePayload,
remotePeer: PeerId
): Promise<PeerId> {
const identityKey = Buffer.from(payload.identityKey as Uint8Array);
const identityKey = Buffer.from(payload.identityKey as Uint8Array)
if (!(await isValidPeerId(remotePeer.id, identityKey))) {
throw new Error("Peer ID doesn't match libp2p public key.");
throw new Error("Peer ID doesn't match libp2p public key.")
}
const generatedPayload = getHandshakePayload(noiseStaticKey);
const generatedPayload = getHandshakePayload(noiseStaticKey)
// Unmarshaling from PublicKey protobuf
const publicKey = keys.unmarshalPublicKey(identityKey);
const publicKey = keys.unmarshalPublicKey(identityKey)
if (!payload.identitySig || !publicKey.verify(generatedPayload, Buffer.from(payload.identitySig))) {
throw new Error("Static key doesn't match to peer that signed payload!");
throw new Error("Static key doesn't match to peer that signed payload!")
}
return PeerId.createFromPubKey(identityKey);
return PeerId.createFromPubKey(identityKey)
}
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);
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);
const k1 = okm.slice(0, 32)
const k2 = okm.slice(32, 64)
const k3 = okm.slice(64, 96)
return [k1, k2, k3];
return [k1, k2, k3]
}
export function isValidPublicKey(pk: bytes): boolean {
return x25519.publicKeyVerify(pk.slice(0, 32));
export function isValidPublicKey (pk: bytes): boolean {
return x25519.publicKeyVerify(pk.slice(0, 32))
}

18
test/fixtures/peer.ts vendored
View File

@ -1,4 +1,4 @@
import PeerId from 'peer-id';
import PeerId from 'peer-id'
// ed25519 keys
const peers = [{
@ -17,20 +17,20 @@ const peers = [{
id: '12D3KooWPCofiCjhdtezP4eMnqBjjutFZNHjV39F5LWNrCvaLnzT',
privKey: 'CAESYLhUut01XPu+yIPbtZ3WnxOd26FYuTMRn/BbdFYsZE2KxueKRlo9yIAxmFReoNFUKztUU4G2aUiTbqDQaA6i0MDG54pGWj3IgDGYVF6g0VQrO1RTgbZpSJNuoNBoDqLQwA==',
pubKey: 'CAESIMbnikZaPciAMZhUXqDRVCs7VFOBtmlIk26g0GgOotDA'
}];
}]
export async function createPeerIdsFromFixtures (length) {
return Promise.all(
export async function createPeerIdsFromFixtures (length:number): Promise<PeerId[]> {
return await Promise.all(
Array.from({ length }).map((_, i) => PeerId.createFromJSON(peers[i]))
)
}
export async function createPeerIds (length) {
const peerIds: any[] = [];
export async function createPeerIds (length: number): Promise<PeerId[]> {
const peerIds: PeerId[] = []
for (let i = 0; i < length; i++) {
const id = await PeerId.create({ keyType: 'ed25519', bits: 256 });
peerIds.push(id);
const id = await PeerId.create({ keyType: 'Ed25519', bits: 256 })
peerIds.push(id)
}
return peerIds;
return peerIds
}

View File

@ -1,67 +1,65 @@
import {Buffer} from "buffer";
import {IK} 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";
import { Buffer } from 'buffer'
import { IK } 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("IK handshake", () => {
const prologue = Buffer.alloc(0);
describe('IK handshake', () => {
const prologue = Buffer.alloc(0)
it("Test complete IK handshake", async () => {
it('Test complete IK handshake', async () => {
try {
const ikI = new IK();
const ikR = new IK();
const ikI = new IK()
const ikR = new IK()
// Generate static noise keys
const kpInitiator: KeyPair = await generateKeypair();
const kpResponder: KeyPair = await generateKeypair();
const kpInitiator: KeyPair = await generateKeypair()
const kpResponder: KeyPair = await generateKeypair()
// Generate libp2p keys
const libp2pInitKeys = await generateEd25519Keys();
const libp2pRespKeys = await generateEd25519Keys();
const libp2pInitKeys = await generateEd25519Keys()
const libp2pRespKeys = await generateEd25519Keys()
// Create sessions
const initiatorSession = await ikI.initSession(true, prologue, kpInitiator, kpResponder.publicKey);
const responderSession = await ikR.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 */
// 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, initSignedPayload);
const initSignedPayload = await libp2pInitKeys.sign(getHandshakePayload(kpInitiator.publicKey))
libp2pInitKeys.marshal().slice(0, 32)
const libp2pInitPubKey = libp2pInitKeys.marshal().slice(32, 64)
const payloadInitEnc = await createHandshakePayload(libp2pInitPubKey, initSignedPayload)
// initiator sends message
const message = Buffer.concat([Buffer.alloc(0), payloadInitEnc]);
const messageBuffer = ikI.sendMessage(initiatorSession, message);
const message = Buffer.concat([Buffer.alloc(0), payloadInitEnc])
const messageBuffer = ikI.sendMessage(initiatorSession, message)
expect(messageBuffer.ne.length).not.equal(0);
expect(messageBuffer.ne.length).not.equal(0)
// responder receives message
const plaintext = ikR.recvMessage(responderSession, messageBuffer);
ikR.recvMessage(responderSession, messageBuffer)
/* 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, respSignedPayload);
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, respSignedPayload)
const message1 = Buffer.concat([message, payloadRespEnc]);
const messageBuffer2 = ikR.sendMessage(responderSession, message1);
const message1 = Buffer.concat([message, payloadRespEnc])
const messageBuffer2 = ikR.sendMessage(responderSession, message1)
// 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));
ikI.recvMessage(initiatorSession, messageBuffer2)
assert(initiatorSession?.cs1?.k.equals(responderSession?.cs1?.k || new Uint8Array()))
assert(initiatorSession?.cs2?.k.equals(responderSession?.cs2?.k || new Uint8Array()))
} catch (e) {
console.error(e);
return assert(false, e.message);
return assert(false, e.message)
}
});
});
})
})

View File

@ -1,143 +1,143 @@
import { expect, assert } from "chai";
import { Buffer } from 'buffer';
import { expect, assert } from 'chai'
import { Buffer } from 'buffer'
import { XX } from "../../src/handshakes/xx";
import { KeyPair } from "../../src/@types/libp2p";
import { generateEd25519Keys } from "../utils";
import {createHandshakePayload, generateKeypair, getHandshakePayload, getHkdf} from "../../src/utils";
import { XX } from '../../src/handshakes/xx'
import { KeyPair } from '../../src/@types/libp2p'
import { generateEd25519Keys } from '../utils'
import { createHandshakePayload, generateKeypair, getHandshakePayload, getHkdf } from '../../src/utils'
describe("XX Handshake", () => {
const prologue = Buffer.alloc(0);
describe('XX Handshake', () => {
const prologue = Buffer.alloc(0)
it("Test creating new XX session", async () => {
it('Test creating new XX session', async () => {
try {
const xx = new XX();
const xx = new XX()
const kpInitiator: KeyPair = await generateKeypair();
const kpResponder: KeyPair = await generateKeypair();
const kpInitiator: KeyPair = await generateKeypair()
await generateKeypair()
const session = await xx.initSession(true, prologue, kpInitiator);
await xx.initSession(true, prologue, kpInitiator)
} catch (e) {
assert(false, e.message);
assert(false, e.message)
}
});
})
it("Test get HKDF", async () => {
const ckBytes = Buffer.from('4e6f6973655f58585f32353531395f58436861436861506f6c795f53484132353600000000000000000000000000000000000000000000000000000000000000', 'hex');
const ikm = Buffer.from('a3eae50ea37a47e8a7aa0c7cd8e16528670536dcd538cebfd724fb68ce44f1910ad898860666227d4e8dd50d22a9a64d1c0a6f47ace092510161e9e442953da3', 'hex');
const ck = Buffer.alloc(32);
ckBytes.copy(ck);
it('Test get HKDF', () => {
const ckBytes = Buffer.from('4e6f6973655f58585f32353531395f58436861436861506f6c795f53484132353600000000000000000000000000000000000000000000000000000000000000', 'hex')
const ikm = Buffer.from('a3eae50ea37a47e8a7aa0c7cd8e16528670536dcd538cebfd724fb68ce44f1910ad898860666227d4e8dd50d22a9a64d1c0a6f47ace092510161e9e442953da3', 'hex')
const ck = Buffer.alloc(32)
ckBytes.copy(ck)
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');
});
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')
})
async function doHandshake(xx) {
const kpInit = await generateKeypair();
const kpResp = await generateKeypair();
async function doHandshake (xx) {
const kpInit = await generateKeypair()
const kpResp = await generateKeypair()
// initiator setup
const libp2pInitKeys = await generateEd25519Keys();
const initSignedPayload = await libp2pInitKeys.sign(getHandshakePayload(kpInit.publicKey));
const libp2pInitKeys = await generateEd25519Keys()
const initSignedPayload = await libp2pInitKeys.sign(getHandshakePayload(kpInit.publicKey))
// responder setup
const libp2pRespKeys = await generateEd25519Keys();
const respSignedPayload = await libp2pRespKeys.sign(getHandshakePayload(kpResp.publicKey));
const libp2pRespKeys = await generateEd25519Keys()
const respSignedPayload = await libp2pRespKeys.sign(getHandshakePayload(kpResp.publicKey))
// initiator: new XX noise session
const nsInit = xx.initSession(true, prologue, kpInit);
const nsInit = xx.initSession(true, prologue, kpInit)
// responder: new XX noise session
const nsResp = xx.initSession(false, prologue, kpResp);
const nsResp = xx.initSession(false, prologue, kpResp)
/* STAGE 0 */
// initiator creates payload
const libp2pInitPrivKey = libp2pInitKeys.marshal().slice(0, 32);
const libp2pInitPubKey = libp2pInitKeys.marshal().slice(32, 64);
libp2pInitKeys.marshal().slice(0, 32)
const libp2pInitPubKey = libp2pInitKeys.marshal().slice(32, 64)
const payloadInitEnc = await createHandshakePayload(libp2pInitPubKey, initSignedPayload);
const payloadInitEnc = await createHandshakePayload(libp2pInitPubKey, initSignedPayload)
// initiator sends message
const message = Buffer.concat([Buffer.alloc(0), payloadInitEnc]);
const messageBuffer = xx.sendMessage(nsInit, message);
const message = Buffer.concat([Buffer.alloc(0), payloadInitEnc])
const messageBuffer = xx.sendMessage(nsInit, message)
expect(messageBuffer.ne.length).not.equal(0);
expect(messageBuffer.ne.length).not.equal(0)
// responder receives message
xx.recvMessage(nsResp, messageBuffer);
xx.recvMessage(nsResp, messageBuffer)
/* STAGE 1 */
// responder creates payload
const libp2pRespPrivKey = libp2pRespKeys.marshal().slice(0, 32);
const libp2pRespPubKey = libp2pRespKeys.marshal().slice(32, 64);
const payloadRespEnc = await createHandshakePayload(libp2pRespPubKey, respSignedPayload);
libp2pRespKeys.marshal().slice(0, 32)
const libp2pRespPubKey = libp2pRespKeys.marshal().slice(32, 64)
const payloadRespEnc = await createHandshakePayload(libp2pRespPubKey, respSignedPayload)
const message1 = Buffer.concat([message, payloadRespEnc]);
const messageBuffer2 = xx.sendMessage(nsResp, message1);
const message1 = Buffer.concat([message, payloadRespEnc])
const messageBuffer2 = xx.sendMessage(nsResp, message1)
expect(messageBuffer2.ne.length).not.equal(0);
expect(messageBuffer2.ns.length).not.equal(0);
expect(messageBuffer2.ne.length).not.equal(0)
expect(messageBuffer2.ns.length).not.equal(0)
// initiator receive payload
xx.recvMessage(nsInit, messageBuffer2);
xx.recvMessage(nsInit, messageBuffer2)
/* STAGE 2 */
// initiator send message
const messageBuffer3 = xx.sendMessage(nsInit, Buffer.alloc(0));
const messageBuffer3 = xx.sendMessage(nsInit, Buffer.alloc(0))
// responder receive message
xx.recvMessage(nsResp, messageBuffer3);
xx.recvMessage(nsResp, messageBuffer3)
assert(nsInit.cs1.k.equals(nsResp.cs1.k));
assert(nsInit.cs2.k.equals(nsResp.cs2.k));
assert(nsInit.cs1.k.equals(nsResp.cs1.k))
assert(nsInit.cs2.k.equals(nsResp.cs2.k))
return { nsInit, nsResp };
return { nsInit, nsResp }
}
it("Test handshake", async () => {
it('Test handshake', async () => {
try {
const xx = new XX();
await doHandshake(xx);
const xx = new XX()
await doHandshake(xx)
} catch (e) {
assert(false, e.message);
assert(false, e.message)
}
});
})
it("Test symmetric encrypt and decrypt", async () => {
it('Test symmetric encrypt and decrypt', async () => {
try {
const xx = new XX();
const { nsInit, nsResp } = await doHandshake(xx);
const ad = Buffer.from("authenticated");
const message = Buffer.from("HelloCrypto");
const xx = new XX()
const { nsInit, nsResp } = await doHandshake(xx)
const ad = Buffer.from('authenticated')
const message = Buffer.from('HelloCrypto')
const ciphertext = xx.encryptWithAd(nsInit.cs1, ad, message);
assert(!Buffer.from("HelloCrypto").equals(ciphertext), "Encrypted message should not be same as plaintext.");
const {plaintext: decrypted, valid} = xx.decryptWithAd(nsResp.cs1, ad, ciphertext);
const ciphertext = xx.encryptWithAd(nsInit.cs1, ad, message)
assert(!Buffer.from('HelloCrypto').equals(ciphertext), 'Encrypted message should not be same as plaintext.')
const { plaintext: decrypted, valid } = xx.decryptWithAd(nsResp.cs1, ad, ciphertext)
assert(Buffer.from("HelloCrypto").equals(decrypted), "Decrypted text not equal to original message.");
assert(valid);
assert(Buffer.from('HelloCrypto').equals(decrypted), 'Decrypted text not equal to original message.')
assert(valid)
} catch (e) {
assert(false, e.message);
assert(false, e.message)
}
});
})
it("Test multiple messages encryption and decryption", async () => {
const xx = new XX();
const { nsInit, nsResp } = await doHandshake(xx);
const ad = Buffer.from("authenticated");
const message = Buffer.from("ethereum1");
it('Test multiple messages encryption and decryption', async () => {
const xx = new XX()
const { nsInit, nsResp } = await doHandshake(xx)
const ad = Buffer.from('authenticated')
const message = Buffer.from('ethereum1')
const encrypted = xx.encryptWithAd(nsInit.cs1, ad, message);
const {plaintext: decrypted} = xx.decryptWithAd(nsResp.cs1, ad, encrypted);
assert.equal("ethereum1", decrypted.toString("utf8"), "Decrypted text not equal to original message.");
const encrypted = xx.encryptWithAd(nsInit.cs1, ad, message)
const { plaintext: decrypted } = xx.decryptWithAd(nsResp.cs1, ad, encrypted)
assert.equal('ethereum1', decrypted.toString('utf8'), 'Decrypted text not equal to original message.')
const message2 = Buffer.from("ethereum2");
const encrypted2 = xx.encryptWithAd(nsInit.cs1, ad, message2);
const {plaintext: decrypted2} = xx.decryptWithAd(nsResp.cs1, ad, encrypted2);
assert.equal("ethereum2", decrypted2.toString("utf-8"), "Decrypted text not equal to original message.");
});
});
const message2 = Buffer.from('ethereum2')
const encrypted2 = xx.encryptWithAd(nsInit.cs1, ad, message2)
const { plaintext: decrypted2 } = xx.decryptWithAd(nsResp.cs1, ad, encrypted2)
assert.equal('ethereum2', decrypted2.toString('utf-8'), 'Decrypted text not equal to original message.')
})
})

View File

@ -1,80 +1,79 @@
import Wrap from "it-pb-rpc";
import Duplex from 'it-pair/duplex';
import {Buffer} from "buffer";
import {assert, expect} from "chai";
import Wrap from 'it-pb-rpc'
import Duplex from 'it-pair/duplex'
import { Buffer } from 'buffer'
import { assert, expect } from 'chai'
import {createPeerIdsFromFixtures} from "./fixtures/peer";
import {generateKeypair, getPayload} from "../src/utils";
import {IKHandshake} from "../src/handshake-ik";
import { createPeerIdsFromFixtures } from './fixtures/peer'
import { generateKeypair, getPayload } from '../src/utils'
import { IKHandshake } from '../src/handshake-ik'
describe("IK Handshake", () => {
let peerA, peerB, fakePeer;
describe('IK Handshake', () => {
let peerA, peerB
before(async () => {
[peerA, peerB, fakePeer] = await createPeerIdsFromFixtures(3);
});
[peerA, peerB] = await createPeerIdsFromFixtures(3)
})
it("should finish both stages as initiator and responder", async() => {
it('should finish both stages as initiator and responder', async () => {
try {
const duplex = Duplex();
const connectionFrom = Wrap(duplex[0]);
const connectionTo = Wrap(duplex[1]);
const duplex = Duplex()
const connectionFrom = Wrap(duplex[0])
const connectionTo = Wrap(duplex[1])
const prologue = Buffer.alloc(0);
const staticKeysInitiator = generateKeypair();
const staticKeysResponder = generateKeypair();
const prologue = Buffer.alloc(0)
const staticKeysInitiator = generateKeypair()
const staticKeysResponder = generateKeypair()
const initPayload = await getPayload(peerA, staticKeysInitiator.publicKey);
const handshakeInit = new IKHandshake(true, initPayload, prologue, staticKeysInitiator, connectionFrom, staticKeysResponder.publicKey, peerB);
const initPayload = await getPayload(peerA, staticKeysInitiator.publicKey)
const handshakeInit = new IKHandshake(true, initPayload, prologue, staticKeysInitiator, connectionFrom, staticKeysResponder.publicKey, peerB)
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey);
const handshakeResp = new IKHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, staticKeysInitiator.publicKey);
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey)
const handshakeResp = new IKHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, staticKeysInitiator.publicKey)
await handshakeInit.stage0();
await handshakeResp.stage0();
await handshakeInit.stage0()
await handshakeResp.stage0()
await handshakeResp.stage1();
await handshakeInit.stage1();
await handshakeResp.stage1()
await handshakeInit.stage1()
// Test shared key
if (handshakeInit.session.cs1 && handshakeResp.session.cs1 && handshakeInit.session.cs2 && handshakeResp.session.cs2) {
assert(handshakeInit.session.cs1.k.equals(handshakeResp.session.cs1.k));
assert(handshakeInit.session.cs2.k.equals(handshakeResp.session.cs2.k));
assert(handshakeInit.session.cs1.k.equals(handshakeResp.session.cs1.k))
assert(handshakeInit.session.cs2.k.equals(handshakeResp.session.cs2.k))
} else {
assert(false);
assert(false)
}
// Test encryption and decryption
const encrypted = handshakeInit.encrypt(Buffer.from("encryptthis"), handshakeInit.session);
const {plaintext: decrypted} = handshakeResp.decrypt(encrypted, handshakeResp.session);
assert(decrypted.equals(Buffer.from("encryptthis")));
const encrypted = handshakeInit.encrypt(Buffer.from('encryptthis'), handshakeInit.session)
const { plaintext: decrypted } = handshakeResp.decrypt(encrypted, handshakeResp.session)
assert(decrypted.equals(Buffer.from('encryptthis')))
} catch (e) {
console.error(e);
assert(false, e.message);
assert(false, e.message)
}
});
})
it("should throw error if responder's static key changed", async() => {
it("should throw error if responder's static key changed", async () => {
try {
const duplex = Duplex();
const connectionFrom = Wrap(duplex[0]);
const connectionTo = Wrap(duplex[1]);
const duplex = Duplex()
const connectionFrom = Wrap(duplex[0])
const connectionTo = Wrap(duplex[1])
const prologue = Buffer.alloc(0);
const staticKeysInitiator = generateKeypair();
const staticKeysResponder = generateKeypair();
const oldScammyKeys = generateKeypair();
const prologue = Buffer.alloc(0)
const staticKeysInitiator = generateKeypair()
const staticKeysResponder = generateKeypair()
const oldScammyKeys = generateKeypair()
const initPayload = await getPayload(peerA, staticKeysInitiator.publicKey);
const handshakeInit = new IKHandshake(true, initPayload, prologue, staticKeysInitiator, connectionFrom, oldScammyKeys.publicKey, peerB);
const initPayload = await getPayload(peerA, staticKeysInitiator.publicKey)
const handshakeInit = new IKHandshake(true, initPayload, prologue, staticKeysInitiator, connectionFrom, oldScammyKeys.publicKey, peerB)
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey);
const handshakeResp = new IKHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, staticKeysInitiator.publicKey);
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey)
const handshakeResp = new IKHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, staticKeysInitiator.publicKey)
await handshakeInit.stage0();
await handshakeResp.stage0();
await handshakeInit.stage0()
await handshakeResp.stage0()
} catch (e) {
expect(e.message).to.include("Error occurred while verifying initiator's signed payload");
expect(e.message).to.include("Error occurred while verifying initiator's signed payload")
}
});
});
})
})

View File

@ -1,11 +1,11 @@
import { expect } from "chai";
import { Noise } from "../src";
import { expect } from 'chai'
import { Noise } from '../src'
describe("Index", () => {
it("should expose class with tag and required functions", () => {
const noise = new Noise();
expect(noise.protocol).to.equal('/noise');
expect(typeof(noise.secureInbound)).to.equal('function');
expect(typeof(noise.secureOutbound)).to.equal('function');
describe('Index', () => {
it('should expose class with tag and required functions', () => {
const noise = new Noise()
expect(noise.protocol).to.equal('/noise')
expect(typeof (noise.secureInbound)).to.equal('function')
expect(typeof (noise.secureOutbound)).to.equal('function')
})
});
})

View File

@ -1,34 +1,32 @@
import { expect, assert } from "chai";
import { KeyCache } from "../src/keycache";
import {createPeerIds, createPeerIdsFromFixtures} from "./fixtures/peer";
import { assert } from 'chai'
import { KeyCache } from '../src/keycache'
import { createPeerIds, createPeerIdsFromFixtures } from './fixtures/peer'
describe("KeyCache", () => {
let peerA, peerB;
describe('KeyCache', () => {
let peerA
before(async () => {
[peerA, peerB] = await createPeerIdsFromFixtures(2);
});
[peerA] = await createPeerIdsFromFixtures(2)
})
it("should store and load same key successfully", async() => {
it('should store and load same key successfully', async () => {
try {
const key = Buffer.from("this is id 007");
await KeyCache.store(peerA, key);
const result = await KeyCache.load(peerA);
assert(result.equals(key), "Stored and loaded key are not the same");
const key = Buffer.from('this is id 007')
await KeyCache.store(peerA, key)
const result = await KeyCache.load(peerA)
assert(result?.equals(key), 'Stored and loaded key are not the same')
} catch (e) {
console.error(e);
assert(false, `Test failed - ${e.message}`)
}
});
})
it("should return undefined if key not found", async() => {
it('should return undefined if key not found', async () => {
try {
const [newPeer] = await createPeerIds(1);
const result = await KeyCache.load(newPeer);
assert(!result);
const [newPeer] = await createPeerIds(1)
const result = await KeyCache.load(newPeer)
assert(!result)
} catch (e) {
console.error(e);
assert(false, `Test failed - ${e.message}`)
}
});
});
})
})

View File

@ -1,57 +1,57 @@
import {assert, expect} from "chai";
import DuplexPair from 'it-pair/duplex';
import {createPeerIdsFromFixtures} from "./fixtures/peer";
import Wrap from "it-pb-rpc";
import sinon from "sinon";
import BufferList from "bl";
import {randomBytes} from 'libp2p-crypto';
import {Buffer} from "buffer";
import { assert, expect } from 'chai'
import DuplexPair from 'it-pair/duplex'
import { createPeerIdsFromFixtures } from './fixtures/peer'
import Wrap from 'it-pb-rpc'
import sinon from 'sinon'
import BufferList from 'bl'
import { randomBytes } from 'libp2p-crypto'
import { Buffer } from 'buffer'
import {Noise} from "../src";
import {XXHandshake} from "../src/handshake-xx";
import {createHandshakePayload, generateKeypair, getHandshakePayload, getPayload, signPayload} from "../src/utils";
import {decode0, decode2, encode1, uint16BEDecode, uint16BEEncode} from "../src/encoder";
import {XX} from "../src/handshakes/xx";
import {getKeyPairFromPeerId} from "./utils";
import {KeyCache} from "../src/keycache";
import {NOISE_MSG_MAX_LENGTH_BYTES} from "../src/constants";
import { Noise } from '../src'
import { XXHandshake } from '../src/handshake-xx'
import { createHandshakePayload, generateKeypair, getHandshakePayload, getPayload, signPayload } from '../src/utils'
import { decode0, decode2, encode1, uint16BEDecode, uint16BEEncode } from '../src/encoder'
import { XX } from '../src/handshakes/xx'
import { getKeyPairFromPeerId } from './utils'
import { KeyCache } from '../src/keycache'
import { NOISE_MSG_MAX_LENGTH_BYTES } from '../src/constants'
describe("Noise", () => {
let remotePeer, localPeer;
let sandbox = sinon.createSandbox();
describe('Noise', () => {
let remotePeer, localPeer
const sandbox = sinon.createSandbox()
before(async () => {
[localPeer, remotePeer] = await createPeerIdsFromFixtures(2);
});
[localPeer, remotePeer] = await createPeerIdsFromFixtures(2)
})
afterEach(function() {
sandbox.restore();
});
afterEach(function () {
sandbox.restore()
})
it("should communicate through encrypted streams without noise pipes", async() => {
it('should communicate through encrypted streams without noise pipes', async () => {
try {
const noiseInit = new Noise(undefined, undefined, false);
const noiseResp = new Noise(undefined, undefined, false);
const noiseInit = new Noise(undefined, undefined)
const noiseResp = new Noise(undefined, undefined)
const [inboundConnection, outboundConnection] = DuplexPair();
const [inboundConnection, outboundConnection] = DuplexPair()
const [outbound, inbound] = await Promise.all([
noiseInit.secureOutbound(localPeer, outboundConnection, remotePeer),
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer),
]);
const wrappedInbound = Wrap(inbound.conn);
const wrappedOutbound = Wrap(outbound.conn);
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer)
])
const wrappedInbound = Wrap(inbound.conn)
const wrappedOutbound = Wrap(outbound.conn)
wrappedOutbound.writeLP(Buffer.from("test"));
const response = await wrappedInbound.readLP();
expect(response.toString()).equal("test");
wrappedOutbound.writeLP(Buffer.from('test'))
const response = await wrappedInbound.readLP()
expect(response.toString()).equal('test')
} catch (e) {
assert(false, e.message);
assert(false, e.message)
}
});
})
it("should test that secureOutbound is spec compliant", async() => {
const noiseInit = new Noise(undefined, undefined, false);
const [inboundConnection, outboundConnection] = DuplexPair();
it('should test that secureOutbound is spec compliant', async () => {
const noiseInit = new Noise(undefined, undefined)
const [inboundConnection, outboundConnection] = DuplexPair()
const [outbound, { wrapped, handshake }] = await Promise.all([
noiseInit.secureOutbound(localPeer, outboundConnection, remotePeer),
@ -63,304 +63,295 @@ describe("Noise", () => {
lengthDecoder: uint16BEDecode,
maxDataLength: NOISE_MSG_MAX_LENGTH_BYTES
}
);
const prologue = Buffer.alloc(0);
const staticKeys = generateKeypair();
const xx = new XX();
)
const prologue = Buffer.alloc(0)
const staticKeys = generateKeypair()
const xx = new XX()
const payload = await getPayload(remotePeer, staticKeys.publicKey);
const handshake = new XXHandshake(false, payload, prologue, staticKeys, wrapped, localPeer, xx);
const payload = await getPayload(remotePeer, staticKeys.publicKey)
const handshake = new XXHandshake(false, payload, prologue, staticKeys, wrapped, localPeer, xx)
let receivedMessageBuffer = decode0((await wrapped.readLP()).slice());
let receivedMessageBuffer = decode0((await wrapped.readLP()).slice())
// The first handshake message contains the initiator's ephemeral public key
expect(receivedMessageBuffer.ne.length).equal(32);
xx.recvMessage(handshake.session, receivedMessageBuffer);
expect(receivedMessageBuffer.ne.length).equal(32)
xx.recvMessage(handshake.session, receivedMessageBuffer)
// Stage 1
const { publicKey: libp2pPubKey } = getKeyPairFromPeerId(remotePeer);
const signedPayload = await signPayload(remotePeer, getHandshakePayload(staticKeys.publicKey));
const handshakePayload = await createHandshakePayload(libp2pPubKey, signedPayload);
const { publicKey: libp2pPubKey } = getKeyPairFromPeerId(remotePeer)
const signedPayload = await signPayload(remotePeer, getHandshakePayload(staticKeys.publicKey))
const handshakePayload = await createHandshakePayload(libp2pPubKey, signedPayload)
const messageBuffer = xx.sendMessage(handshake.session, handshakePayload);
wrapped.writeLP(encode1(messageBuffer));
const messageBuffer = xx.sendMessage(handshake.session, handshakePayload)
wrapped.writeLP(encode1(messageBuffer))
// Stage 2 - finish handshake
receivedMessageBuffer = decode2((await wrapped.readLP()).slice());
xx.recvMessage(handshake.session, receivedMessageBuffer);
return {wrapped, handshake};
})(),
]);
receivedMessageBuffer = decode2((await wrapped.readLP()).slice())
xx.recvMessage(handshake.session, receivedMessageBuffer)
return { wrapped, handshake }
})()
])
try {
const wrappedOutbound = Wrap(outbound.conn);
wrappedOutbound.write(new BufferList([Buffer.from("test")]));
const wrappedOutbound = Wrap(outbound.conn)
wrappedOutbound.write(new BufferList([Buffer.from('test')]))
// Check that noise message is prefixed with 16-bit big-endian unsigned integer
const receivedEncryptedPayload = (await wrapped.read()).slice();
const dataLength = receivedEncryptedPayload.readInt16BE(0);
const data = receivedEncryptedPayload.slice(2, dataLength + 2);
const {plaintext: decrypted, valid} = handshake.decrypt(data, handshake.session);
const receivedEncryptedPayload = (await wrapped.read()).slice()
const dataLength = receivedEncryptedPayload.readInt16BE(0)
const data = receivedEncryptedPayload.slice(2, dataLength + 2)
const { plaintext: decrypted, valid } = handshake.decrypt(data, handshake.session)
// Decrypted data should match
assert(decrypted.equals(Buffer.from("test")));
assert(valid);
assert(decrypted.equals(Buffer.from('test')))
assert(valid)
} catch (e) {
assert(false, e.message);
assert(false, e.message)
}
});
})
it("should test large payloads", async function() {
this.timeout(10000);
it('should test large payloads', async function () {
this.timeout(10000)
try {
const noiseInit = new Noise(undefined, undefined, false);
const noiseResp = new Noise(undefined, undefined, false);
const noiseInit = new Noise(undefined, undefined)
const noiseResp = new Noise(undefined, undefined)
const [inboundConnection, outboundConnection] = DuplexPair();
const [inboundConnection, outboundConnection] = DuplexPair()
const [outbound, inbound] = await Promise.all([
noiseInit.secureOutbound(localPeer, outboundConnection, remotePeer),
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer),
]);
const wrappedInbound = Wrap(inbound.conn);
const wrappedOutbound = Wrap(outbound.conn);
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer)
])
const wrappedInbound = Wrap(inbound.conn)
const wrappedOutbound = Wrap(outbound.conn)
const largePlaintext = randomBytes(100000);
wrappedOutbound.writeLP(largePlaintext);
const response = await wrappedInbound.read(100000);
const largePlaintext = randomBytes(100000)
wrappedOutbound.writeLP(largePlaintext)
const response = await wrappedInbound.read(100000)
expect(response.length).equals(largePlaintext.length);
expect(response.length).equals(largePlaintext.length)
} catch (e) {
console.log(e);
assert(false, e.message);
assert(false, e.message)
}
});
})
it.skip("should communicate through encrypted streams with noise pipes", async() => {
it.skip('should communicate through encrypted streams with noise pipes', async () => {
try {
const staticKeysInitiator = generateKeypair();
const noiseInit = new Noise(staticKeysInitiator.privateKey);
const staticKeysResponder = generateKeypair();
const noiseResp = new Noise(staticKeysResponder.privateKey);
const staticKeysInitiator = generateKeypair()
const noiseInit = new Noise(staticKeysInitiator.privateKey)
const staticKeysResponder = generateKeypair()
const noiseResp = new Noise(staticKeysResponder.privateKey)
// Prepare key cache for noise pipes
KeyCache.store(localPeer, staticKeysInitiator.publicKey);
KeyCache.store(remotePeer, staticKeysResponder.publicKey);
KeyCache.store(localPeer, staticKeysInitiator.publicKey)
KeyCache.store(remotePeer, staticKeysResponder.publicKey)
const xxSpy = sandbox.spy(noiseInit, "performXXHandshake");
const xxFallbackSpy = sandbox.spy(noiseInit, "performXXFallbackHandshake");
const xxSpy = sandbox.spy(noiseInit, 'performXXHandshake')
const xxFallbackSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
const [inboundConnection, outboundConnection] = DuplexPair();
const [inboundConnection, outboundConnection] = DuplexPair()
const [outbound, inbound] = await Promise.all([
noiseInit.secureOutbound(localPeer, outboundConnection, remotePeer),
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer),
]);
const wrappedInbound = Wrap(inbound.conn);
const wrappedOutbound = Wrap(outbound.conn);
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer)
])
const wrappedInbound = Wrap(inbound.conn)
const wrappedOutbound = Wrap(outbound.conn)
wrappedOutbound.writeLP(Buffer.from("test v2"));
const response = await wrappedInbound.readLP();
expect(response.toString()).equal("test v2");
wrappedOutbound.writeLP(Buffer.from('test v2'))
const response = await wrappedInbound.readLP()
expect(response.toString()).equal('test v2')
assert(xxSpy.notCalled);
assert(xxFallbackSpy.notCalled);
assert(xxSpy.notCalled)
assert(xxFallbackSpy.notCalled)
} catch (e) {
console.error(e);
assert(false, e.message);
assert(false, e.message)
}
});
})
it.skip("IK -> XX fallback: initiator has invalid remote static key", async() => {
it.skip('IK -> XX fallback: initiator has invalid remote static key', async () => {
try {
const staticKeysInitiator = generateKeypair();
const noiseInit = new Noise(staticKeysInitiator.privateKey);
const noiseResp = new Noise();
const xxSpy = sandbox.spy(noiseInit, "performXXFallbackHandshake");
const staticKeysInitiator = generateKeypair()
const noiseInit = new Noise(staticKeysInitiator.privateKey)
const noiseResp = new Noise()
const xxSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
// Prepare key cache for noise pipes
KeyCache.resetStorage();
KeyCache.store(localPeer, staticKeysInitiator.publicKey);
KeyCache.store(remotePeer, generateKeypair().publicKey);
KeyCache.resetStorage()
KeyCache.store(localPeer, staticKeysInitiator.publicKey)
KeyCache.store(remotePeer, generateKeypair().publicKey)
const [inboundConnection, outboundConnection] = DuplexPair();
const [inboundConnection, outboundConnection] = DuplexPair()
const [outbound, inbound] = await Promise.all([
noiseInit.secureOutbound(localPeer, outboundConnection, remotePeer),
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer),
]);
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer)
])
const wrappedInbound = Wrap(inbound.conn);
const wrappedOutbound = Wrap(outbound.conn);
const wrappedInbound = Wrap(inbound.conn)
const wrappedOutbound = Wrap(outbound.conn)
wrappedOutbound.writeLP(Buffer.from("test fallback"));
const response = await wrappedInbound.readLP();
expect(response.toString()).equal("test fallback");
wrappedOutbound.writeLP(Buffer.from('test fallback'))
const response = await wrappedInbound.readLP()
expect(response.toString()).equal('test fallback')
assert(xxSpy.calledOnce, "XX Fallback method was never called.");
assert(xxSpy.calledOnce, 'XX Fallback method was never called.')
} catch (e) {
console.error(e);
assert(false, e.message);
assert(false, e.message)
}
});
})
//this didn't work before but we didn't verify decryption
it.skip("IK -> XX fallback: responder has disabled noise pipes", async() => {
try {
const staticKeysInitiator = generateKeypair();
const noiseInit = new Noise(staticKeysInitiator.privateKey);
const staticKeysResponder = generateKeypair();
const noiseResp = new Noise(staticKeysResponder.privateKey, undefined, false);
const xxSpy = sandbox.spy(noiseInit, "performXXFallbackHandshake");
// Prepare key cache for noise pipes
KeyCache.store(localPeer, staticKeysInitiator.publicKey);
KeyCache.store(remotePeer, staticKeysResponder.publicKey);
const [inboundConnection, outboundConnection] = DuplexPair();
const [outbound, inbound] = await Promise.all([
noiseInit.secureOutbound(localPeer, outboundConnection, remotePeer),
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer),
]);
const wrappedInbound = Wrap(inbound.conn);
const wrappedOutbound = Wrap(outbound.conn);
wrappedOutbound.writeLP(Buffer.from("test fallback"));
const response = await wrappedInbound.readLP();
expect(response.toString()).equal("test fallback");
assert(xxSpy.calledOnce, "XX Fallback method was never called.");
} catch (e) {
console.error(e);
assert(false, e.message);
}
});
it.skip("Initiator starts with XX (pipes disabled), responder has enabled noise pipes", async() => {
// this didn't work before but we didn't verify decryption
it.skip('IK -> XX fallback: responder has disabled noise pipes', async () => {
try {
const staticKeysInitiator = generateKeypair();
const noiseInit = new Noise(staticKeysInitiator.privateKey, undefined, false);
const staticKeysResponder = generateKeypair();
const staticKeysInitiator = generateKeypair()
const noiseInit = new Noise(staticKeysInitiator.privateKey)
const noiseResp = new Noise(staticKeysResponder.privateKey);
const xxInitSpy = sandbox.spy(noiseInit, "performXXHandshake");
const xxRespSpy = sandbox.spy(noiseResp, "performXXFallbackHandshake");
const staticKeysResponder = generateKeypair()
const noiseResp = new Noise(staticKeysResponder.privateKey, undefined, false)
const xxSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
// Prepare key cache for noise pipes
KeyCache.store(localPeer, staticKeysInitiator.publicKey);
const [inboundConnection, outboundConnection] = DuplexPair();
KeyCache.store(localPeer, staticKeysInitiator.publicKey)
KeyCache.store(remotePeer, staticKeysResponder.publicKey)
const [inboundConnection, outboundConnection] = DuplexPair()
const [outbound, inbound] = await Promise.all([
noiseInit.secureOutbound(localPeer, outboundConnection, remotePeer),
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer),
]);
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer)
])
const wrappedInbound = Wrap(inbound.conn);
const wrappedOutbound = Wrap(outbound.conn);
const wrappedInbound = Wrap(inbound.conn)
const wrappedOutbound = Wrap(outbound.conn)
wrappedOutbound.writeLP(Buffer.from("test fallback"));
const response = await wrappedInbound.readLP();
expect(response.toString()).equal("test fallback");
wrappedOutbound.writeLP(Buffer.from('test fallback'))
const response = await wrappedInbound.readLP()
expect(response.toString()).equal('test fallback')
assert(xxInitSpy.calledOnce, "XX method was never called.");
assert(xxRespSpy.calledOnce, "XX Fallback method was never called.");
assert(xxSpy.calledOnce, 'XX Fallback method was never called.')
} catch (e) {
console.error(e);
assert(false, e.message);
assert(false, e.message)
}
});
})
it.skip("IK: responder has no remote static key", async() => {
it.skip('Initiator starts with XX (pipes disabled), responder has enabled noise pipes', async () => {
try {
const staticKeysInitiator = generateKeypair();
const noiseInit = new Noise(staticKeysInitiator.privateKey);
const staticKeysResponder = generateKeypair();
const staticKeysInitiator = generateKeypair()
const noiseInit = new Noise(staticKeysInitiator.privateKey, undefined, false)
const staticKeysResponder = generateKeypair()
const noiseResp = new Noise(staticKeysResponder.privateKey);
const ikInitSpy = sandbox.spy(noiseInit, "performIKHandshake");
const xxFallbackInitSpy = sandbox.spy(noiseInit, "performXXFallbackHandshake");
const ikRespSpy = sandbox.spy(noiseResp, "performIKHandshake");
const noiseResp = new Noise(staticKeysResponder.privateKey)
const xxInitSpy = sandbox.spy(noiseInit, 'performXXHandshake')
const xxRespSpy = sandbox.spy(noiseResp, 'performXXFallbackHandshake')
// Prepare key cache for noise pipes
KeyCache.resetStorage();
KeyCache.store(remotePeer, staticKeysResponder.publicKey);
KeyCache.store(localPeer, staticKeysInitiator.publicKey)
const [inboundConnection, outboundConnection] = DuplexPair();
const [inboundConnection, outboundConnection] = DuplexPair()
const [outbound, inbound] = await Promise.all([
noiseInit.secureOutbound(localPeer, outboundConnection, remotePeer),
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer),
]);
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer)
])
const wrappedInbound = Wrap(inbound.conn);
const wrappedOutbound = Wrap(outbound.conn);
const wrappedInbound = Wrap(inbound.conn)
const wrappedOutbound = Wrap(outbound.conn)
wrappedOutbound.writeLP(Buffer.from("test fallback"));
const response = await wrappedInbound.readLP();
expect(response.toString()).equal("test fallback");
wrappedOutbound.writeLP(Buffer.from('test fallback'))
const response = await wrappedInbound.readLP()
expect(response.toString()).equal('test fallback')
assert(ikInitSpy.calledOnce, "IK handshake was not called.");
assert(ikRespSpy.calledOnce, "IK handshake was not called.");
assert(xxFallbackInitSpy.notCalled, "XX Fallback method was called.");
assert(xxInitSpy.calledOnce, 'XX method was never called.')
assert(xxRespSpy.calledOnce, 'XX Fallback method was never called.')
} catch (e) {
console.error(e);
assert(false, e.message);
assert(false, e.message)
}
});
})
it("should working without remote peer provided in incoming connection", async() => {
it.skip('IK: responder has no remote static key', async () => {
try {
const staticKeysInitiator = generateKeypair();
const noiseInit = new Noise(staticKeysInitiator.privateKey);
const staticKeysResponder = generateKeypair();
const noiseResp = new Noise(staticKeysResponder.privateKey);
const staticKeysInitiator = generateKeypair()
const noiseInit = new Noise(staticKeysInitiator.privateKey)
const staticKeysResponder = generateKeypair()
const noiseResp = new Noise(staticKeysResponder.privateKey)
const ikInitSpy = sandbox.spy(noiseInit, 'performIKHandshake')
const xxFallbackInitSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
const ikRespSpy = sandbox.spy(noiseResp, 'performIKHandshake')
// Prepare key cache for noise pipes
KeyCache.store(localPeer, staticKeysInitiator.publicKey);
KeyCache.store(remotePeer, staticKeysResponder.publicKey);
KeyCache.resetStorage()
KeyCache.store(remotePeer, staticKeysResponder.publicKey)
const [inboundConnection, outboundConnection] = DuplexPair()
const [inboundConnection, outboundConnection] = DuplexPair();
const [outbound, inbound] = await Promise.all([
noiseInit.secureOutbound(localPeer, outboundConnection, remotePeer),
noiseResp.secureInbound(remotePeer, inboundConnection),
]);
const wrappedInbound = Wrap(inbound.conn);
const wrappedOutbound = Wrap(outbound.conn);
noiseResp.secureInbound(remotePeer, inboundConnection, localPeer)
])
wrappedOutbound.writeLP(Buffer.from("test v2"));
const response = await wrappedInbound.readLP();
expect(response.toString()).equal("test v2");
const wrappedInbound = Wrap(inbound.conn)
const wrappedOutbound = Wrap(outbound.conn)
assert(inbound.remotePeer.marshalPubKey().equals(localPeer.marshalPubKey()));
assert(outbound.remotePeer.marshalPubKey().equals(remotePeer.marshalPubKey()));
wrappedOutbound.writeLP(Buffer.from('test fallback'))
const response = await wrappedInbound.readLP()
expect(response.toString()).equal('test fallback')
assert(ikInitSpy.calledOnce, 'IK handshake was not called.')
assert(ikRespSpy.calledOnce, 'IK handshake was not called.')
assert(xxFallbackInitSpy.notCalled, 'XX Fallback method was called.')
} catch (e) {
console.error(e);
assert(false, e.message);
assert(false, e.message)
}
});
})
it("should accept and return early data from remote peer", async() => {
it('should working without remote peer provided in incoming connection', async () => {
try {
const staticKeysInitiator = generateKeypair()
const noiseInit = new Noise(staticKeysInitiator.privateKey)
const staticKeysResponder = generateKeypair()
const noiseResp = new Noise(staticKeysResponder.privateKey)
// Prepare key cache for noise pipes
KeyCache.store(localPeer, staticKeysInitiator.publicKey)
KeyCache.store(remotePeer, staticKeysResponder.publicKey)
const [inboundConnection, outboundConnection] = DuplexPair()
const [outbound, inbound] = await Promise.all([
noiseInit.secureOutbound(localPeer, outboundConnection, remotePeer),
noiseResp.secureInbound(remotePeer, inboundConnection)
])
const wrappedInbound = Wrap(inbound.conn)
const wrappedOutbound = Wrap(outbound.conn)
wrappedOutbound.writeLP(Buffer.from('test v2'))
const response = await wrappedInbound.readLP()
expect(response.toString()).equal('test v2')
assert(inbound.remotePeer.marshalPubKey().equals(localPeer.marshalPubKey()))
assert(outbound.remotePeer.marshalPubKey().equals(remotePeer.marshalPubKey()))
} catch (e) {
assert(false, e.message)
}
})
it('should accept and return early data from remote peer', async () => {
try {
const localPeerEarlyData = Buffer.from('early data')
const staticKeysInitiator = generateKeypair();
const noiseInit = new Noise(staticKeysInitiator.privateKey, localPeerEarlyData);
const staticKeysResponder = generateKeypair();
const noiseResp = new Noise(staticKeysResponder.privateKey);
const staticKeysInitiator = generateKeypair()
const noiseInit = new Noise(staticKeysInitiator.privateKey, localPeerEarlyData)
const staticKeysResponder = generateKeypair()
const noiseResp = new Noise(staticKeysResponder.privateKey)
// Prepare key cache for noise pipes
KeyCache.store(localPeer, staticKeysInitiator.publicKey);
KeyCache.store(remotePeer, staticKeysResponder.publicKey);
KeyCache.store(localPeer, staticKeysInitiator.publicKey)
KeyCache.store(remotePeer, staticKeysResponder.publicKey)
const [inboundConnection, outboundConnection] = DuplexPair();
const [inboundConnection, outboundConnection] = DuplexPair()
const [outbound, inbound] = await Promise.all([
noiseInit.secureOutbound(localPeer, outboundConnection, remotePeer),
noiseResp.secureInbound(remotePeer, inboundConnection),
]);
noiseResp.secureInbound(remotePeer, inboundConnection)
])
assert(inbound.remoteEarlyData.equals(localPeerEarlyData))
assert(outbound.remoteEarlyData.equals(Buffer.alloc(0)))
} catch (e) {
console.error(e);
assert(false, e.message);
assert(false, e.message)
}
});
});
})
})

View File

@ -1,14 +1,14 @@
import {keys} from 'libp2p-crypto';
import {KeyPair} from "../src/@types/libp2p";
import PeerId from "peer-id";
import { keys, PrivateKey } from 'libp2p-crypto'
import { KeyPair } from '../src/@types/libp2p'
import PeerId from 'peer-id'
export async function generateEd25519Keys() {
return await keys.generateKeyPair('ed25519');
export async function generateEd25519Keys (): Promise<PrivateKey> {
return await keys.generateKeyPair('Ed25519', 32)
}
export function getKeyPairFromPeerId(peerId: PeerId): KeyPair {
export function getKeyPairFromPeerId (peerId: PeerId): KeyPair {
return {
privateKey: peerId.privKey.marshal().slice(0, 32),
publicKey: peerId.marshalPubKey(),
publicKey: peerId.marshalPubKey()
}
}

View File

@ -1,74 +1,73 @@
import Wrap from "it-pb-rpc";
import {Buffer} from "buffer";
import Duplex from 'it-pair/duplex';
import Wrap from 'it-pb-rpc'
import { Buffer } from 'buffer'
import Duplex from 'it-pair/duplex'
import {
generateKeypair,
getPayload,
} from "../src/utils";
import {XXFallbackHandshake} from "../src/handshake-xx-fallback";
import {createPeerIdsFromFixtures} from "./fixtures/peer";
import {assert} from "chai";
import {decode1, encode0, encode1} from "../src/encoder";
getPayload
} from '../src/utils'
import { XXFallbackHandshake } from '../src/handshake-xx-fallback'
import { createPeerIdsFromFixtures } from './fixtures/peer'
import { assert } from 'chai'
import { encode0 } from '../src/encoder'
describe("XX Fallback Handshake", () => {
let peerA, peerB, fakePeer;
describe('XX Fallback Handshake', () => {
let peerA, peerB
before(async () => {
[peerA, peerB] = await createPeerIdsFromFixtures(2);
});
[peerA, peerB] = await createPeerIdsFromFixtures(2)
})
it("should test that both parties can fallback to XX and finish handshake", async () => {
it('should test that both parties can fallback to XX and finish handshake', async () => {
try {
const duplex = Duplex();
const connectionFrom = Wrap(duplex[0]);
const connectionTo = Wrap(duplex[1]);
const duplex = Duplex()
const connectionFrom = Wrap(duplex[0])
const connectionTo = Wrap(duplex[1])
const prologue = Buffer.alloc(0);
const staticKeysInitiator = generateKeypair();
const staticKeysResponder = generateKeypair();
const ephemeralKeys = generateKeypair();
const prologue = Buffer.alloc(0)
const staticKeysInitiator = generateKeypair()
const staticKeysResponder = generateKeypair()
const ephemeralKeys = generateKeypair()
// Initial msg for responder is IK first message from initiator
const handshakePayload = await getPayload(peerA, staticKeysInitiator.publicKey);
const handshakePayload = await getPayload(peerA, staticKeysInitiator.publicKey)
const initialMsgR = encode0({
ne: ephemeralKeys.publicKey,
ns: Buffer.alloc(0),
ciphertext: handshakePayload,
});
ciphertext: handshakePayload
})
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey);
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey)
const handshakeResp =
new XXFallbackHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, initialMsgR, peerA);
new XXFallbackHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, initialMsgR, peerA)
await handshakeResp.propose();
await handshakeResp.exchange();
await handshakeResp.propose()
await handshakeResp.exchange()
// Initial message for initiator is XX Message B from responder
// This is the point where initiator falls back from IK
const initialMsgI = await connectionFrom.readLP();
const initialMsgI = await connectionFrom.readLP()
const handshakeInit =
new XXFallbackHandshake(true, handshakePayload, prologue, staticKeysInitiator, connectionFrom, initialMsgI, peerB, ephemeralKeys);
new XXFallbackHandshake(true, handshakePayload, prologue, staticKeysInitiator, connectionFrom, initialMsgI, peerB, ephemeralKeys)
await handshakeInit.propose();
await handshakeInit.exchange();
await handshakeInit.propose()
await handshakeInit.exchange()
await handshakeInit.finish();
await handshakeResp.finish();
await handshakeInit.finish()
await handshakeResp.finish()
const sessionInitator = handshakeInit.session;
const sessionResponder = handshakeResp.session;
const sessionInitator = handshakeInit.session
const sessionResponder = handshakeResp.session
// Test shared key
if (sessionInitator.cs1 && sessionResponder.cs1 && sessionInitator.cs2 && sessionResponder.cs2) {
assert(sessionInitator.cs1.k.equals(sessionResponder.cs1.k));
assert(sessionInitator.cs2.k.equals(sessionResponder.cs2.k));
assert(sessionInitator.cs1.k.equals(sessionResponder.cs1.k))
assert(sessionInitator.cs2.k.equals(sessionResponder.cs2.k))
} else {
assert(false);
assert(false)
}
} catch (e) {
console.error(e);
assert(false, e.message);
assert(false, e.message)
}
});
})
})

View File

@ -1,121 +1,120 @@
import {assert, expect} from "chai";
import Duplex from 'it-pair/duplex';
import {Buffer} from "buffer";
import Wrap from "it-pb-rpc";
import {XXHandshake} from "../src/handshake-xx";
import {generateKeypair, getPayload} from "../src/utils";
import {createPeerIdsFromFixtures} from "./fixtures/peer";
import { assert, expect } from 'chai'
import Duplex from 'it-pair/duplex'
import { Buffer } from 'buffer'
import Wrap from 'it-pb-rpc'
import { XXHandshake } from '../src/handshake-xx'
import { generateKeypair, getPayload } from '../src/utils'
import { createPeerIdsFromFixtures } from './fixtures/peer'
describe("XX Handshake", () => {
let peerA, peerB, fakePeer;
describe('XX Handshake', () => {
let peerA, peerB, fakePeer
before(async () => {
[peerA, peerB, fakePeer] = await createPeerIdsFromFixtures(3);
});
[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 connectionFrom = Wrap(duplex[0]);
const connectionTo = Wrap(duplex[1]);
const duplex = Duplex()
const connectionFrom = Wrap(duplex[0])
const connectionTo = Wrap(duplex[1])
const prologue = Buffer.alloc(0);
const staticKeysInitiator = generateKeypair();
const staticKeysResponder = generateKeypair();
const prologue = Buffer.alloc(0)
const staticKeysInitiator = generateKeypair()
const staticKeysResponder = generateKeypair()
const initPayload = await getPayload(peerA, staticKeysInitiator.publicKey);
const handshakeInitator = new XXHandshake(true, initPayload, prologue, staticKeysInitiator, connectionFrom, peerB);
const initPayload = await getPayload(peerA, staticKeysInitiator.publicKey)
const handshakeInitator = new XXHandshake(true, initPayload, prologue, staticKeysInitiator, connectionFrom, peerB)
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey);
const handshakeResponder = new XXHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, peerA);
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey)
const handshakeResponder = new XXHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, peerA)
await handshakeInitator.propose();
await handshakeResponder.propose();
await handshakeInitator.propose()
await handshakeResponder.propose()
await handshakeResponder.exchange();
await handshakeInitator.exchange();
await handshakeResponder.exchange()
await handshakeInitator.exchange()
await handshakeInitator.finish();
await handshakeResponder.finish();
await handshakeInitator.finish()
await handshakeResponder.finish()
const sessionInitator = handshakeInitator.session;
const sessionResponder = handshakeResponder.session;
const sessionInitator = handshakeInitator.session
const sessionResponder = handshakeResponder.session
// Test shared key
if (sessionInitator.cs1 && sessionResponder.cs1 && sessionInitator.cs2 && sessionResponder.cs2) {
assert(sessionInitator.cs1.k.equals(sessionResponder.cs1.k));
assert(sessionInitator.cs2.k.equals(sessionResponder.cs2.k));
assert(sessionInitator.cs1.k.equals(sessionResponder.cs1.k))
assert(sessionInitator.cs2.k.equals(sessionResponder.cs2.k))
} else {
assert(false);
assert(false)
}
// Test encryption and decryption
const encrypted = handshakeInitator.encrypt(Buffer.from("encryptthis"), handshakeInitator.session);
const {plaintext: decrypted, valid} = handshakeResponder.decrypt(encrypted, handshakeResponder.session);
assert(decrypted.equals(Buffer.from("encryptthis")));
assert(valid);
const encrypted = handshakeInitator.encrypt(Buffer.from('encryptthis'), handshakeInitator.session)
const { plaintext: decrypted, valid } = handshakeResponder.decrypt(encrypted, handshakeResponder.session)
assert(decrypted.equals(Buffer.from('encryptthis')))
assert(valid)
} catch (e) {
assert(false, e.message);
assert(false, e.message)
}
});
})
it("Initiator should fail to exchange handshake if given wrong public key in payload", async() => {
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 duplex = Duplex()
const connectionFrom = Wrap(duplex[0])
const connectionTo = Wrap(duplex[1])
const prologue = Buffer.alloc(0);
const staticKeysInitiator = generateKeypair();
const staticKeysResponder = generateKeypair();
const prologue = Buffer.alloc(0)
const staticKeysInitiator = generateKeypair()
const staticKeysResponder = generateKeypair()
const initPayload = await getPayload(peerA, staticKeysInitiator.publicKey);
const handshakeInitator = new XXHandshake(true, initPayload, prologue, staticKeysInitiator, connectionFrom, fakePeer);
const initPayload = await getPayload(peerA, staticKeysInitiator.publicKey)
const handshakeInitator = new XXHandshake(true, initPayload, prologue, staticKeysInitiator, connectionFrom, fakePeer)
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey);
const handshakeResponder = new XXHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, peerA);
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey)
const handshakeResponder = new XXHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, peerA)
await handshakeInitator.propose();
await handshakeResponder.propose();
await handshakeInitator.propose()
await handshakeResponder.propose()
await handshakeResponder.exchange();
await handshakeInitator.exchange();
await handshakeResponder.exchange()
await handshakeInitator.exchange()
assert(false, "Should throw exception");
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() => {
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 duplex = Duplex()
const connectionFrom = Wrap(duplex[0])
const connectionTo = Wrap(duplex[1])
const prologue = Buffer.alloc(0);
const staticKeysInitiator = generateKeypair();
const staticKeysResponder = generateKeypair();
const prologue = Buffer.alloc(0)
const staticKeysInitiator = generateKeypair()
const staticKeysResponder = generateKeypair()
const initPayload = await getPayload(peerA, staticKeysInitiator.publicKey);
const handshakeInitator = new XXHandshake(true, initPayload, prologue, staticKeysInitiator, connectionFrom, peerB);
const initPayload = await getPayload(peerA, staticKeysInitiator.publicKey)
const handshakeInitator = new XXHandshake(true, initPayload, prologue, staticKeysInitiator, connectionFrom, peerB)
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey);
const handshakeResponder = new XXHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, fakePeer);
const respPayload = await getPayload(peerB, staticKeysResponder.publicKey)
const handshakeResponder = new XXHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, fakePeer)
await handshakeInitator.propose();
await handshakeResponder.propose();
await handshakeInitator.propose()
await handshakeResponder.propose()
await handshakeResponder.exchange();
await handshakeInitator.exchange();
await handshakeResponder.exchange()
await handshakeInitator.exchange()
await handshakeInitator.finish();
await handshakeResponder.finish();
await handshakeInitator.finish()
await handshakeResponder.finish()
assert(false, "Should throw exception");
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

@ -4,6 +4,7 @@
"module": "commonjs",
"strict": true,
"allowJs": true,
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"noImplicitAny": false,

View File

@ -1803,9 +1803,10 @@ add-stream@^1.0.0:
resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa"
integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=
aegir@ipfs/aegir#feat/cosmiconfig:
version "24.0.0"
resolved "https://codeload.github.com/ipfs/aegir/tar.gz/15d895f22055023a78d704c8beabbd45c35a8f61"
aegir@25.0.0:
version "25.0.0"
resolved "https://registry.yarnpkg.com/aegir/-/aegir-25.0.0.tgz#9275c4dba0717355ee20c1c35cb27f474fadcca9"
integrity sha512-VP1ACVfnUj/k/kwc/W74IOFKVurPsN3BmWxtbxVpr7hLkOoi3/OYBaaROeVndmUiOHihye1PLQUAMpHROgRpeg==
dependencies:
"@babel/cli" "^7.10.1"
"@babel/core" "^7.10.2"
@ -1849,7 +1850,6 @@ aegir@ipfs/aegir#feat/cosmiconfig:
eslint-plugin-node "^11.0.0"
eslint-plugin-promise "^4.2.1"
eslint-plugin-standard "^4.0.1"
esm "^3.2.25"
execa "^4.0.0"
extract-zip "^2.0.1"
findup-sync "^4.0.0"
@ -4595,11 +4595,6 @@ eslint@^6.3.0:
text-table "^0.2.0"
v8-compile-cache "^2.0.3"
esm@^3.2.25:
version "3.2.25"
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
espree@^6.1.2:
version "6.2.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a"