mirror of
https://github.com/fluencelabs/js-libp2p-noise
synced 2025-04-25 14:32:18 +00:00
Create interface according to spec
This commit is contained in:
parent
8cf420fd88
commit
d2c844d598
18
src/handshake.ts
Normal file
18
src/handshake.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
67
src/noise.ts
67
src/noise.ts
@ -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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
21
src/utils.ts
Normal 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);
|
||||||
|
}
|
25
src/xx.ts
25
src/xx.ts
@ -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;
|
||||||
|
@ -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');
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -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();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user