Create interface according to spec

This commit is contained in:
morrigan 2019-11-11 21:58:04 +01:00
parent 8cf420fd88
commit d2c844d598
8 changed files with 147 additions and 49 deletions

18
src/handshake.ts Normal file
View File

@ -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<NoiseSession> {
const xx = new XXHandshake();
const nsInit = await xx.initSession(isInitiator, prologue, staticKeys, remotePublicKey);
// TODO: exchange handshake messages and confirm handshake
return nsInit;
}
}

View File

@ -1,12 +1,13 @@
import { x25519 } from 'bcrypto'; import { x25519 } from 'bcrypto';
import { Buffer } from "buffer";
import { bytes } from "./types/basic"; import { bytes } from "./types/basic";
import { Connection } from "./types/libp2p"; import { InsecureConnection, NoiseConnection, PeerId, SecureConnection, KeyPair } from "./types/libp2p";
import { KeyPair, XXHandshake } from "./xx";
import { signPayload } from "../test/utils";
import {Buffer} from "buffer";
export class Noise { import { Handshake } from "./handshake";
import { generateKeypair, signPayload } from "./utils";
export class Noise implements NoiseConnection {
private readonly privateKey: bytes; private readonly privateKey: bytes;
private staticKeys?: KeyPair; private staticKeys?: KeyPair;
private earlyData?: bytes; private earlyData?: bytes;
@ -24,20 +25,39 @@ export class Noise {
} }
} }
public tag() { public protocol() {
return '/noise'; return '/noise';
} }
public async encrypt(InsecureConnection: Connection, remotePublicKey: bytes) { // encrypt outgoing data to the remote party (handshake as initiator)
const isInitiator = InsecureConnection.stats.direction === "outbound"; public async secureOutbound(connection: InsecureConnection, remotePeer: PeerId) : Promise<SecureConnection> {
const secretKey = await this.doHandshake(isInitiator, remotePublicKey); 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<SecureConnection> {
}
private async read(ciphertext: bytes) {
} }
private async doHandshake(isInitiator: boolean, remotePublicKey: bytes) : Promise<bytes> { private async write(plaintext: bytes) {
const xx = new XXHandshake();
}
private async createSecureConnection(
connection: InsecureConnection,
remotePublicKey: bytes,
isInitiator: boolean,
) : Promise<SecureConnection> {
if (!this.staticKeys) { if (!this.staticKeys) {
this.staticKeys = await xx.generateKeypair(); this.staticKeys = await generateKeypair();
} }
let signedPayload; let signedPayload;
@ -46,10 +66,25 @@ export class Noise {
signedPayload = await signPayload(this.privateKey, payload); signedPayload = await signPayload(this.privateKey, payload);
} }
const prologue = Buffer.from(this.tag()); const prologue = Buffer.from(this.protocol());
const nsInit = await xx.initSession(isInitiator, prologue, this.staticKeys, remotePublicKey); const session = await Handshake.runXX(isInitiator, remotePublicKey, prologue, signedPayload, this.staticKeys);
// TODO: Send messages, confirm handshake and return shared key
return Buffer.alloc(0); 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,
}
} }
} }

View File

@ -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, id: string,
privKey: string, privKey: string,
pubKey: string, pubKey: string,
}; };
type PeerInfo = {
noiseKey: bytes32,
libp2pKey: bytes,
};
type ConnectionStats = { type ConnectionStats = {
direction: "inbound" | "outbound", direction: "inbound" | "outbound",
encryption: string, encryption: string,
} }
export interface Connection { type Stream = {
sink(),
source: Object,
}
export interface InsecureConnection {
localPeer: PeerId, localPeer: PeerId,
remotePeer: PeerId, remotePeer: PeerId,
stats: ConnectionStats, stats: ConnectionStats,
streams(): [Stream],
addStream(muxedStream: any) : Stream,
}
export interface NoiseConnection {
remoteEarlyData?(): bytes,
secureOutbound(insecure: InsecureConnection, remotePeer: PeerId): Promise<SecureConnection>,
secureInbound(insecure: InsecureConnection): Promise<SecureConnection>,
}
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,
} }

21
src/utils.ts Normal file
View File

@ -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<KeyPair> {
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);
}

View File

@ -1,12 +1,11 @@
import {bytes32, bytes16, uint32, uint64, bytes} from './types/basic'
import { Buffer } from 'buffer'; import { Buffer } from 'buffer';
import { AEAD, x25519, HKDF, SHA256 } from 'bcrypto'; import { AEAD, x25519, HKDF, SHA256 } from 'bcrypto';
import { BN } from 'bn.js'; import { BN } from 'bn.js';
export interface KeyPair { import { bytes32, uint32, uint64, bytes } from './types/basic'
publicKey: bytes32, import { KeyPair } from './types/libp2p'
privateKey: bytes32, import { generateKeypair } from './utils';
}
interface MessageBuffer { interface MessageBuffer {
ne: bytes32, ne: bytes32,
@ -34,7 +33,7 @@ type HandshakeState = {
psk: bytes32, psk: bytes32,
} }
type NoiseSession = { export type NoiseSession = {
hs: HandshakeState, hs: HandshakeState,
h?: bytes32, h?: bytes32,
cs1?: CipherState, cs1?: CipherState,
@ -227,7 +226,7 @@ export class XXHandshake {
private async writeMessageA(hs: HandshakeState, payload: bytes) : Promise<MessageBuffer> { private async writeMessageA(hs: HandshakeState, payload: bytes) : Promise<MessageBuffer> {
let ns = Buffer.alloc(0); let ns = Buffer.alloc(0);
hs.e = await this.generateKeypair(); hs.e = await generateKeypair();
const ne = hs.e.publicKey; const ne = hs.e.publicKey;
this.mixHash(hs.ss, ne); this.mixHash(hs.ss, ne);
@ -237,7 +236,7 @@ export class XXHandshake {
} }
private async writeMessageB(hs: HandshakeState, payload: bytes) : Promise<MessageBuffer> { private async writeMessageB(hs: HandshakeState, payload: bytes) : Promise<MessageBuffer> {
hs.e = await this.generateKeypair(); hs.e = await generateKeypair();
const ne = hs.e.publicKey; const ne = hs.e.publicKey;
this.mixHash(hs.ss, ne); this.mixHash(hs.ss, ne);
@ -318,16 +317,6 @@ export class XXHandshake {
return this.decryptWithAd(cs, Buffer.alloc(0), message.ciphertext); return this.decryptWithAd(cs, Buffer.alloc(0), message.ciphertext);
} }
public async generateKeypair() : Promise<KeyPair> {
const privateKey = x25519.privateKeyGenerate();
const publicKey = x25519.publicKeyCreate(privateKey);
return {
publicKey,
privateKey,
}
}
public async initSession(initiator: boolean, prologue: bytes32, s: KeyPair, rs: bytes32) : Promise<NoiseSession> { public async initSession(initiator: boolean, prologue: bytes32, s: KeyPair, rs: bytes32) : Promise<NoiseSession> {
const psk = this.createEmptyKey(); const psk = this.createEmptyKey();
let hs; let hs;

View File

@ -2,9 +2,10 @@ import { expect } from "chai";
import { Noise } from "../src"; import { Noise } from "../src";
describe("Index", () => { 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")); const noise = new Noise(Buffer.from("privatekey"));
expect(noise.tag()).to.equal('/noise'); expect(noise.protocol()).to.equal('/noise');
expect(typeof(noise.encrypt)).to.equal('function'); expect(typeof(noise.secureInbound)).to.equal('function');
expect(typeof(noise.secureOutbound)).to.equal('function');
}) })
}); });

View File

@ -3,10 +3,10 @@ import { Noise } from "../src";
import {generateEd25519Keys} from "./utils"; import {generateEd25519Keys} from "./utils";
describe("Noise", () => { describe("Noise", () => {
it("should encrypt", async() => { it("should encrypt outgoing data using secureOutbound", async() => {
const libp2pKeys = await generateEd25519Keys(); const libp2pKeys = await generateEd25519Keys();
const noise = new Noise(libp2pKeys._key); const noise = new Noise(libp2pKeys._key);
await noise.encrypt(); await noise.secureOutbound();
}) })
}); });

View File

@ -1,7 +1,6 @@
import protobuf from "protobufjs"; import protobuf from "protobufjs";
import * as crypto from 'libp2p-crypto'; import * as crypto from 'libp2p-crypto';
import { ed25519 } from 'bcrypto'; import { ed25519 } from 'bcrypto';
import { bytes } from "../src/types/basic";
export async function loadPayloadProto () { export async function loadPayloadProto () {
const payloadProtoBuf = await protobuf.load("protos/payload.proto"); const payloadProtoBuf = await protobuf.load("protos/payload.proto");
@ -11,9 +10,3 @@ export async function loadPayloadProto () {
export async function generateEd25519Keys() { export async function generateEd25519Keys() {
return await crypto.keys.generateKeyPair('ed25519'); 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);
}