2019-11-11 15:39:09 +01:00
|
|
|
import { x25519 } from 'bcrypto';
|
2019-11-11 21:58:04 +01:00
|
|
|
import { Buffer } from "buffer";
|
2019-11-20 15:21:53 +01:00
|
|
|
import Wrap from 'it-pb-rpc';
|
2019-11-08 14:03:34 +01:00
|
|
|
|
2019-11-11 21:58:04 +01:00
|
|
|
import { Handshake } from "./handshake";
|
2019-11-20 22:52:08 +01:00
|
|
|
import { createHandshakePayload, generateKeypair, getHandshakePayload, signPayload } from "./utils";
|
2019-11-20 13:23:36 +01:00
|
|
|
import { decryptStreams, encryptStreams } from "./crypto";
|
2019-11-20 15:21:53 +01:00
|
|
|
import { bytes } from "./@types/basic";
|
|
|
|
import { NoiseConnection, PeerId, KeyPair, SecureOutbound } from "./@types/libp2p";
|
|
|
|
import { Duplex } from "./@types/it-pair";
|
2019-11-11 21:58:04 +01:00
|
|
|
|
|
|
|
export class Noise implements NoiseConnection {
|
2019-11-20 13:23:36 +01:00
|
|
|
public protocol = "/noise";
|
|
|
|
|
2019-11-11 15:39:09 +01:00
|
|
|
private readonly privateKey: bytes;
|
2019-11-12 14:02:59 +01:00
|
|
|
private staticKeys: KeyPair;
|
2019-11-11 15:39:09 +01:00
|
|
|
private earlyData?: bytes;
|
|
|
|
|
2019-11-08 14:03:34 +01:00
|
|
|
constructor(privateKey: bytes, staticNoiseKey?: bytes, earlyData?: bytes) {
|
2019-11-11 15:39:09 +01:00
|
|
|
this.privateKey = privateKey;
|
|
|
|
this.earlyData = earlyData;
|
2019-11-08 14:03:34 +01:00
|
|
|
|
2019-11-11 15:39:09 +01:00
|
|
|
if (staticNoiseKey) {
|
|
|
|
const publicKey = x25519.publicKeyCreate(staticNoiseKey);
|
|
|
|
this.staticKeys = {
|
|
|
|
privateKey: staticNoiseKey,
|
|
|
|
publicKey,
|
|
|
|
}
|
2019-11-12 14:02:59 +01:00
|
|
|
} else {
|
|
|
|
// todo: generate new static key
|
2019-11-11 15:39:09 +01:00
|
|
|
}
|
2019-11-08 14:03:34 +01:00
|
|
|
}
|
|
|
|
|
2019-11-20 13:23:36 +01:00
|
|
|
/**
|
|
|
|
* 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 {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> {
|
2019-11-20 15:21:53 +01:00
|
|
|
const wrappedConnection = Wrap(connection);
|
2019-11-20 13:23:36 +01:00
|
|
|
const remotePublicKey = Buffer.from(remotePeer.pubKey);
|
2019-11-20 15:21:53 +01:00
|
|
|
const session = await this.createSecureConnection(wrappedConnection, remotePublicKey, true);
|
2019-11-11 21:58:04 +01:00
|
|
|
|
2019-11-20 13:23:36 +01:00
|
|
|
return {
|
|
|
|
conn: session,
|
|
|
|
remotePeer,
|
2019-11-11 21:58:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-20 13:23:36 +01:00
|
|
|
/**
|
|
|
|
* Decrypt incoming data (handshake as responder).
|
|
|
|
* @param {PeerId} localPeer - PeerId of the receiving peer.
|
|
|
|
* @param 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> {
|
2019-11-11 21:58:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private async createSecureConnection(
|
2019-11-20 15:21:53 +01:00
|
|
|
connection,
|
2019-11-11 21:58:04 +01:00
|
|
|
remotePublicKey: bytes,
|
|
|
|
isInitiator: boolean,
|
2019-11-20 13:23:36 +01:00
|
|
|
) : Promise<Duplex> {
|
2019-11-11 15:39:09 +01:00
|
|
|
if (!this.staticKeys) {
|
2019-11-11 21:58:04 +01:00
|
|
|
this.staticKeys = await generateKeypair();
|
2019-11-11 15:39:09 +01:00
|
|
|
}
|
|
|
|
|
2019-11-20 13:23:36 +01:00
|
|
|
const prologue = Buffer.from(this.protocol);
|
2019-11-21 13:38:39 +01:00
|
|
|
const handshake = new Handshake('XX', remotePublicKey, prologue, this.staticKeys, connection);
|
|
|
|
|
|
|
|
const session = await handshake.propose(isInitiator, this.earlyData);
|
|
|
|
await handshake.exchange(isInitiator, session);
|
|
|
|
await handshake.finish(isInitiator, session);
|
2019-11-11 21:58:04 +01:00
|
|
|
|
2019-11-20 13:23:36 +01:00
|
|
|
return await encryptStreams(connection, session);
|
2019-11-08 14:03:34 +01:00
|
|
|
}
|
|
|
|
|
2019-11-11 21:58:04 +01:00
|
|
|
|
2019-11-08 14:03:34 +01:00
|
|
|
}
|