diff --git a/src/handshake.ts b/src/handshake.ts new file mode 100644 index 0000000..567f734 --- /dev/null +++ b/src/handshake.ts @@ -0,0 +1,18 @@ +import {bytes, bytes32} from "./types/basic"; +import {KeyPair, NoiseSession, XXHandshake} from "./xx"; + +export class Handshake { + static async runXX( + isInitiator: boolean, + remotePublicKey: bytes, + prologue: bytes32, + signedPayload: bytes, + staticKeys: KeyPair, + ) : Promise { + const xx = new XXHandshake(); + + const nsInit = await xx.initSession(isInitiator, prologue, staticKeys, remotePublicKey); + // TODO: exchange handshake messages and confirm handshake + return nsInit; + } +} diff --git a/src/noise.ts b/src/noise.ts index 4e6631b..3af12e9 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -1,12 +1,13 @@ import { x25519 } from 'bcrypto'; +import { Buffer } from "buffer"; import { bytes } from "./types/basic"; -import { Connection } from "./types/libp2p"; -import { KeyPair, XXHandshake } from "./xx"; -import { signPayload } from "../test/utils"; -import {Buffer} from "buffer"; +import { InsecureConnection, NoiseConnection, PeerId, SecureConnection, KeyPair } from "./types/libp2p"; -export class Noise { +import { Handshake } from "./handshake"; +import { generateKeypair, signPayload } from "./utils"; + +export class Noise implements NoiseConnection { private readonly privateKey: bytes; private staticKeys?: KeyPair; private earlyData?: bytes; @@ -24,20 +25,39 @@ export class Noise { } } - public tag() { + public protocol() { return '/noise'; } - public async encrypt(InsecureConnection: Connection, remotePublicKey: bytes) { - const isInitiator = InsecureConnection.stats.direction === "outbound"; - const secretKey = await this.doHandshake(isInitiator, remotePublicKey); + // encrypt outgoing data to the remote party (handshake as initiator) + public async secureOutbound(connection: InsecureConnection, remotePeer: PeerId) : Promise { + try { + const remotePublicKey = Buffer.from(remotePeer.pubKey); + const session = await this.createSecureConnection(connection, remotePublicKey, true); + } catch (e) { + + } + } + + // decrypt incoming data (handshake as responder) + public async secureInbound(connection: InsecureConnection) : Promise { + } + + private async read(ciphertext: bytes) { } - private async doHandshake(isInitiator: boolean, remotePublicKey: bytes) : Promise { - const xx = new XXHandshake(); + private async write(plaintext: bytes) { + + } + + private async createSecureConnection( + connection: InsecureConnection, + remotePublicKey: bytes, + isInitiator: boolean, + ) : Promise { if (!this.staticKeys) { - this.staticKeys = await xx.generateKeypair(); + this.staticKeys = await generateKeypair(); } let signedPayload; @@ -46,10 +66,25 @@ export class Noise { signedPayload = await signPayload(this.privateKey, payload); } - const prologue = Buffer.from(this.tag()); - const nsInit = await xx.initSession(isInitiator, prologue, this.staticKeys, remotePublicKey); - // TODO: Send messages, confirm handshake and return shared key - return Buffer.alloc(0); + const prologue = Buffer.from(this.protocol()); + const session = await Handshake.runXX(isInitiator, remotePublicKey, prologue, signedPayload, this.staticKeys); + + return { + insecure: connection, + initiator: isInitiator, + prologue, + // localKey: get public key, + localPeer: connection.localPeer, + remotePeer: connection.remotePeer, + local: { + noiseKey: this.staticKeys.publicKey, + // libp2pKey: + }, + xxNoiseSession: session, + xxComplete: true, + noiseKeypair: this.staticKeys, + } } + } diff --git a/src/types/libp2p.ts b/src/types/libp2p.ts index 135b82a..2cd1fc3 100644 --- a/src/types/libp2p.ts +++ b/src/types/libp2p.ts @@ -1,18 +1,59 @@ -import { bytes } from "./basic"; +import { bytes, bytes32 } from "./basic"; +import { NoiseSession } from "../xx"; -type PeerId = { +export interface KeyPair { + publicKey: bytes32, + privateKey: bytes32, +} + +export type PeerId = { id: string, privKey: string, pubKey: string, }; +type PeerInfo = { + noiseKey: bytes32, + libp2pKey: bytes, +}; + type ConnectionStats = { direction: "inbound" | "outbound", encryption: string, } -export interface Connection { +type Stream = { + sink(), + source: Object, +} + +export interface InsecureConnection { localPeer: PeerId, remotePeer: PeerId, stats: ConnectionStats, + streams(): [Stream], + addStream(muxedStream: any) : Stream, +} + +export interface NoiseConnection { + remoteEarlyData?(): bytes, + secureOutbound(insecure: InsecureConnection, remotePeer: PeerId): Promise, + secureInbound(insecure: InsecureConnection): Promise, +} + +export interface SecureConnection { + insecure: InsecureConnection, + initiator: boolean, + prologue: bytes32, + localKey: bytes, + localPeer: PeerId, + remotePeer: PeerId, + local: PeerInfo, + remote: PeerInfo, + + xxNoiseSession: NoiseSession, + xxComplete: boolean, + + noiseKeypair: KeyPair, + msgBuffer: bytes, } diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..9b3f4e3 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,21 @@ +import { x25519 } from 'bcrypto'; +import * as crypto from 'libp2p-crypto'; + +import { KeyPair } from "./xx"; +import { bytes } from "./types/basic"; + +export async function generateKeypair() : Promise { + const privateKey = x25519.privateKeyGenerate(); + const publicKey = x25519.publicKeyCreate(privateKey); + + return { + publicKey, + privateKey, + } +} + +export async function signPayload(privateKey: bytes, payload: bytes) { + const Ed25519PrivateKey = crypto.keys.supportedKeys.ed25519.Ed25519PrivateKey; + // const ed25519 = Ed25519PrivateKey(privateKey, "need-to-get-public-key"); + // return ed25519.sign(privateKey, payload); +} diff --git a/src/xx.ts b/src/xx.ts index 261c692..1c51a51 100644 --- a/src/xx.ts +++ b/src/xx.ts @@ -1,12 +1,11 @@ -import {bytes32, bytes16, uint32, uint64, bytes} from './types/basic' import { Buffer } from 'buffer'; import { AEAD, x25519, HKDF, SHA256 } from 'bcrypto'; import { BN } from 'bn.js'; -export interface KeyPair { - publicKey: bytes32, - privateKey: bytes32, -} +import { bytes32, uint32, uint64, bytes } from './types/basic' +import { KeyPair } from './types/libp2p' +import { generateKeypair } from './utils'; + interface MessageBuffer { ne: bytes32, @@ -34,7 +33,7 @@ type HandshakeState = { psk: bytes32, } -type NoiseSession = { +export type NoiseSession = { hs: HandshakeState, h?: bytes32, cs1?: CipherState, @@ -227,7 +226,7 @@ export class XXHandshake { private async writeMessageA(hs: HandshakeState, payload: bytes) : Promise { let ns = Buffer.alloc(0); - hs.e = await this.generateKeypair(); + hs.e = await generateKeypair(); const ne = hs.e.publicKey; this.mixHash(hs.ss, ne); @@ -237,7 +236,7 @@ export class XXHandshake { } private async writeMessageB(hs: HandshakeState, payload: bytes) : Promise { - hs.e = await this.generateKeypair(); + hs.e = await generateKeypair(); const ne = hs.e.publicKey; this.mixHash(hs.ss, ne); @@ -318,16 +317,6 @@ export class XXHandshake { return this.decryptWithAd(cs, Buffer.alloc(0), message.ciphertext); } - public async generateKeypair() : Promise { - const privateKey = x25519.privateKeyGenerate(); - const publicKey = x25519.publicKeyCreate(privateKey); - - return { - publicKey, - privateKey, - } - } - public async initSession(initiator: boolean, prologue: bytes32, s: KeyPair, rs: bytes32) : Promise { const psk = this.createEmptyKey(); let hs; diff --git a/test/index.test.ts b/test/index.test.ts index 6f1fc72..6656a5c 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -2,9 +2,10 @@ import { expect } from "chai"; import { Noise } from "../src"; describe("Index", () => { - it("should expose class with tag and encrypt functions", () => { + it("should expose class with tag and required functions", () => { const noise = new Noise(Buffer.from("privatekey")); - expect(noise.tag()).to.equal('/noise'); - expect(typeof(noise.encrypt)).to.equal('function'); + expect(noise.protocol()).to.equal('/noise'); + expect(typeof(noise.secureInbound)).to.equal('function'); + expect(typeof(noise.secureOutbound)).to.equal('function'); }) }); diff --git a/test/noise.test.ts b/test/noise.test.ts index 86efadd..516298e 100644 --- a/test/noise.test.ts +++ b/test/noise.test.ts @@ -3,10 +3,10 @@ import { Noise } from "../src"; import {generateEd25519Keys} from "./utils"; describe("Noise", () => { - it("should encrypt", async() => { + it("should encrypt outgoing data using secureOutbound", async() => { const libp2pKeys = await generateEd25519Keys(); const noise = new Noise(libp2pKeys._key); - await noise.encrypt(); + await noise.secureOutbound(); }) }); diff --git a/test/utils.ts b/test/utils.ts index 7c9b6cb..9f78703 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -1,7 +1,6 @@ import protobuf from "protobufjs"; import * as crypto from 'libp2p-crypto'; import { ed25519 } from 'bcrypto'; -import { bytes } from "../src/types/basic"; export async function loadPayloadProto () { const payloadProtoBuf = await protobuf.load("protos/payload.proto"); @@ -11,9 +10,3 @@ export async function loadPayloadProto () { export async function generateEd25519Keys() { return await crypto.keys.generateKeyPair('ed25519'); } - -export async function signPayload(privateKey: bytes, payload: bytes) { - const Ed25519PrivateKey = crypto.keys.supportedKeys.ed25519.Ed25519PrivateKey; - // const ed25519 = Ed25519PrivateKey(privateKey, "need-to-get-public-key"); - // return ed25519.sign(privateKey, payload); -}