mirror of
https://github.com/fluencelabs/js-libp2p-noise
synced 2025-08-01 00:12:07 +00:00
Create XX fallback flow
This commit is contained in:
@@ -54,7 +54,11 @@ export class Handshake implements HandshakeInterface {
|
||||
return this.ik.encryptWithAd(cs, Buffer.alloc(0), plaintext);
|
||||
}
|
||||
|
||||
public getRemoteEphemeralKeys(): KeyPair | undefined {
|
||||
public getRemoteEphemeralKeys(): KeyPair {
|
||||
if (!this.session.hs.e) {
|
||||
throw new Error("Ephemeral keys do not exist.");
|
||||
}
|
||||
|
||||
return this.session.hs.e;
|
||||
}
|
||||
|
||||
|
89
src/handshake-xx-fallback.ts
Normal file
89
src/handshake-xx-fallback.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Buffer } from "buffer";
|
||||
|
||||
import { Handshake as XXHandshake } from "./handshake-xx";
|
||||
import { XXHandshake as XX } from "./handshakes/xx";
|
||||
import { KeyPair, PeerId } from "./@types/libp2p";
|
||||
import { bytes, bytes32 } from "./@types/basic";
|
||||
import {
|
||||
createHandshakePayload,
|
||||
getHandshakePayload,
|
||||
signEarlyDataPayload,
|
||||
signPayload,
|
||||
verifySignedPayload,
|
||||
} from "./utils";
|
||||
import { logger } from "./logger";
|
||||
import { decodeMessageBuffer, encodeMessageBuffer } from "./encoder";
|
||||
import { WrappedConnection } from "./noise";
|
||||
|
||||
export class Handshake extends XXHandshake {
|
||||
private ephemeralKeys: KeyPair;
|
||||
private initialMsg: bytes;
|
||||
|
||||
constructor(
|
||||
isInitiator: boolean,
|
||||
libp2pPrivateKey: bytes,
|
||||
libp2pPublicKey: bytes,
|
||||
prologue: bytes32,
|
||||
staticKeys: KeyPair,
|
||||
connection: WrappedConnection,
|
||||
remotePeer: PeerId,
|
||||
ephemeralKeys: KeyPair,
|
||||
initialMsg: bytes,
|
||||
handshake?: XX,
|
||||
) {
|
||||
super(isInitiator, libp2pPrivateKey, libp2pPublicKey, prologue, staticKeys, connection, remotePeer, handshake);
|
||||
this.ephemeralKeys = ephemeralKeys;
|
||||
this.initialMsg = initialMsg;
|
||||
}
|
||||
|
||||
// stage 0
|
||||
public async propose(): Promise<void> {
|
||||
if (this.isInitiator) {
|
||||
logger("XX Fallback Stage 0 - Initiator starting to send first message.");
|
||||
const messageBuffer = this.xx.sendMessage(this.session, Buffer.alloc(0), this.ephemeralKeys);
|
||||
this.connection.writeLP(encodeMessageBuffer(messageBuffer));
|
||||
logger("XX Fallback Stage 0 - Initiator finished sending first message.");
|
||||
} else {
|
||||
logger("XX Fallback Stage 0 - Responder waiting to receive first message...");
|
||||
const receivedMessageBuffer = this.xx.decode0(this.initialMsg);
|
||||
this.xx.recvMessage(this.session, {
|
||||
ne: receivedMessageBuffer.ne,
|
||||
ns: Buffer.alloc(0),
|
||||
ciphertext: Buffer.alloc(0),
|
||||
});
|
||||
logger("XX Fallback Stage 0 - Responder received first message.");
|
||||
}
|
||||
}
|
||||
|
||||
// stage 1
|
||||
public async exchange(): Promise<void> {
|
||||
if (this.isInitiator) {
|
||||
logger('XX Fallback Stage 1 - Initiator waiting to receive first message from responder...');
|
||||
const receivedMessageBuffer = this.xx.decode1(this.initialMsg);
|
||||
const plaintext = this.xx.recvMessage(this.session, receivedMessageBuffer);
|
||||
logger('XX Fallback Stage 1 - Initiator received the message. Got remote\'s static key.');
|
||||
|
||||
// logger("Initiator going to check remote's signature...");
|
||||
// try {
|
||||
// await verifySignedPayload(receivedMessageBuffer.ns, plaintext, this.remotePeer.id);
|
||||
// } catch (e) {
|
||||
// throw new Error(`Error occurred while verifying signed payload: ${e.message}`);
|
||||
// }
|
||||
// logger("All good with the signature!");
|
||||
} else {
|
||||
logger('Stage 1 - Responder sending out first message with signed payload and static key.');
|
||||
const signedPayload = signPayload(this.libp2pPrivateKey, getHandshakePayload(this.staticKeys.publicKey));
|
||||
const signedEarlyDataPayload = signEarlyDataPayload(this.libp2pPrivateKey, Buffer.alloc(0));
|
||||
const handshakePayload = await createHandshakePayload(
|
||||
this.libp2pPublicKey,
|
||||
this.libp2pPrivateKey,
|
||||
signedPayload,
|
||||
signedEarlyDataPayload,
|
||||
);
|
||||
|
||||
const messageBuffer = this.xx.sendMessage(this.session, handshakePayload);
|
||||
this.connection.writeLP(encodeMessageBuffer(messageBuffer));
|
||||
logger('Stage 1 - Responder sent the second handshake message with signed payload.')
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,13 +20,14 @@ export class Handshake implements HandshakeInterface {
|
||||
public isInitiator: boolean;
|
||||
public session: NoiseSession;
|
||||
|
||||
private libp2pPrivateKey: bytes;
|
||||
private libp2pPublicKey: bytes;
|
||||
protected connection: WrappedConnection;
|
||||
protected xx: XXHandshake;
|
||||
|
||||
protected libp2pPrivateKey: bytes;
|
||||
protected libp2pPublicKey: bytes;
|
||||
private prologue: bytes32;
|
||||
private staticKeys: KeyPair;
|
||||
private connection: WrappedConnection;
|
||||
protected staticKeys: KeyPair;
|
||||
private remotePeer: PeerId;
|
||||
private xx: XXHandshake;
|
||||
|
||||
constructor(
|
||||
isInitiator: boolean,
|
||||
@@ -36,7 +37,6 @@ export class Handshake implements HandshakeInterface {
|
||||
staticKeys: KeyPair,
|
||||
connection: WrappedConnection,
|
||||
remotePeer: PeerId,
|
||||
ephemeralKeys?: KeyPair,
|
||||
handshake?: XXHandshake,
|
||||
) {
|
||||
this.isInitiator = isInitiator;
|
||||
|
@@ -199,4 +199,28 @@ export class XXHandshake extends AbstractHandshake {
|
||||
session.mc = session.mc.add(new BN(1));
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
public decode0(input: bytes): MessageBuffer {
|
||||
if (input.length < 32) {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
public decode1(input: bytes): MessageBuffer {
|
||||
if (input.length < 96) {
|
||||
throw new Error("Cannot decode stage 0 MessageBuffer: length less than 96 bytes.");
|
||||
}
|
||||
|
||||
return {
|
||||
ne: input.slice(0, 32),
|
||||
ns: input.slice(32, 80),
|
||||
ciphertext: input.slice(80, input.length),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
94
src/noise.ts
94
src/noise.ts
@@ -8,6 +8,7 @@ import lp from 'it-length-prefixed';
|
||||
|
||||
import { Handshake as XX } from "./handshake-xx";
|
||||
import { Handshake as IK } from "./handshake-ik";
|
||||
import { Handshake as XXFallback } from "./handshake-xx-fallback";
|
||||
import { generateKeypair } from "./utils";
|
||||
import { uint16BEDecode, uint16BEEncode } from "./encoder";
|
||||
import { decryptStream, encryptStream } from "./crypto";
|
||||
@@ -19,6 +20,13 @@ import {HandshakeInterface} from "./@types/handshake-interface";
|
||||
|
||||
export type WrappedConnection = ReturnType<typeof Wrap>;
|
||||
|
||||
type HandshakeParams = {
|
||||
connection: WrappedConnection;
|
||||
isInitiator: boolean;
|
||||
libp2pPublicKey: bytes;
|
||||
remotePeer: PeerId;
|
||||
};
|
||||
|
||||
export class Noise implements NoiseConnection {
|
||||
public protocol = "/noise";
|
||||
|
||||
@@ -52,7 +60,12 @@ export class Noise implements NoiseConnection {
|
||||
public async secureOutbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise<SecureOutbound> {
|
||||
const wrappedConnection = Wrap(connection);
|
||||
const libp2pPublicKey = localPeer.marshalPubKey();
|
||||
const handshake = await this.performHandshake(wrappedConnection, true, libp2pPublicKey, remotePeer);
|
||||
const handshake = await this.performHandshake({
|
||||
connection: wrappedConnection,
|
||||
isInitiator: true,
|
||||
libp2pPublicKey,
|
||||
remotePeer,
|
||||
});
|
||||
const conn = await this.createSecureConnection(wrappedConnection, handshake);
|
||||
|
||||
return {
|
||||
@@ -71,7 +84,12 @@ export class Noise implements NoiseConnection {
|
||||
public async secureInbound(localPeer: PeerId, connection: any, remotePeer: PeerId): Promise<SecureOutbound> {
|
||||
const wrappedConnection = Wrap(connection);
|
||||
const libp2pPublicKey = localPeer.marshalPubKey();
|
||||
const handshake = await this.performHandshake(wrappedConnection, false, libp2pPublicKey, remotePeer);
|
||||
const handshake = await this.performHandshake({
|
||||
connection: wrappedConnection,
|
||||
isInitiator: false,
|
||||
libp2pPublicKey,
|
||||
remotePeer
|
||||
});
|
||||
const conn = await this.createSecureConnection(wrappedConnection, handshake);
|
||||
|
||||
return {
|
||||
@@ -88,46 +106,72 @@ export class Noise implements NoiseConnection {
|
||||
* @param libp2pPublicKey
|
||||
* @param remotePeer
|
||||
*/
|
||||
private async performHandshake(
|
||||
connection: WrappedConnection,
|
||||
isInitiator: boolean,
|
||||
libp2pPublicKey: bytes,
|
||||
remotePeer: PeerId,
|
||||
): Promise<HandshakeInterface> {
|
||||
|
||||
private async performHandshake(params: HandshakeParams): Promise<HandshakeInterface> {
|
||||
// TODO: Implement noise pipes
|
||||
|
||||
const IKhandshake = new IK(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer);
|
||||
|
||||
if(true) {
|
||||
// XX fallback
|
||||
const ephemeralKeys = IKhandshake.getRemoteEphemeralKeys();
|
||||
return await this.performXXHandshake(connection, isInitiator, libp2pPublicKey, remotePeer, ephemeralKeys);
|
||||
if (false) {
|
||||
let IKhandshake;
|
||||
try {
|
||||
IKhandshake = await this.performIKHandshake(params);
|
||||
return IKhandshake;
|
||||
} catch (e) {
|
||||
// XX fallback
|
||||
const ephemeralKeys = IKhandshake.getRemoteEphemeralKeys();
|
||||
return await this.performXXFallbackHandshake(params, ephemeralKeys, e.initialMsg);
|
||||
}
|
||||
} else {
|
||||
return await this.performXXHandshake(connection, isInitiator, libp2pPublicKey, remotePeer);
|
||||
return await this.performXXHandshake(params);
|
||||
}
|
||||
}
|
||||
|
||||
private async performXXHandshake(
|
||||
connection: WrappedConnection,
|
||||
isInitiator: boolean,
|
||||
libp2pPublicKey: bytes,
|
||||
remotePeer: PeerId,
|
||||
ephemeralKeys?: KeyPair,
|
||||
): Promise<HandshakeInterface> {
|
||||
const handshake = new XX(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer, ephemeralKeys);
|
||||
private async performXXFallbackHandshake(
|
||||
params: HandshakeParams,
|
||||
ephemeralKeys: KeyPair,
|
||||
initialMsg: bytes,
|
||||
): Promise<XXFallback> {
|
||||
const { isInitiator, libp2pPublicKey, remotePeer, connection } = params;
|
||||
const handshake =
|
||||
new XXFallback(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer, ephemeralKeys, initialMsg);
|
||||
|
||||
try {
|
||||
await handshake.propose();
|
||||
await handshake.exchange();
|
||||
await handshake.finish(this.earlyData);
|
||||
} catch (e) {
|
||||
throw new Error(`Error occurred during handshake: ${e.message}`);
|
||||
throw new Error(`Error occurred during XX Fallback handshake: ${e.message}`);
|
||||
}
|
||||
|
||||
return handshake;
|
||||
}
|
||||
|
||||
private async performXXHandshake(
|
||||
params: HandshakeParams,
|
||||
): Promise<XX> {
|
||||
const { isInitiator, libp2pPublicKey, remotePeer, connection } = params;
|
||||
const handshake = new XX(isInitiator, this.privateKey, libp2pPublicKey, this.prologue, this.staticKeys, connection, remotePeer);
|
||||
|
||||
try {
|
||||
await handshake.propose();
|
||||
await handshake.exchange();
|
||||
await handshake.finish(this.earlyData);
|
||||
} catch (e) {
|
||||
throw new Error(`Error occurred during XX handshake: ${e.message}`);
|
||||
}
|
||||
|
||||
return handshake;
|
||||
}
|
||||
|
||||
private async performIKHandshake(
|
||||
params: HandshakeParams,
|
||||
): Promise<IK> {
|
||||
const { isInitiator, libp2pPublicKey, remotePeer, connection } = params;
|
||||
const handshake = new IK(params.isInitiator, this.privateKey, params.libp2pPublicKey, this.prologue, this.staticKeys, params.connection, remotePeer);
|
||||
|
||||
// TODO
|
||||
|
||||
return handshake;
|
||||
}
|
||||
|
||||
private async createSecureConnection(
|
||||
connection: WrappedConnection,
|
||||
handshake: HandshakeInterface,
|
||||
|
@@ -61,7 +61,7 @@ describe("Noise", () => {
|
||||
const xx = new XXHandshake();
|
||||
const { privateKey: libp2pPrivKey, publicKey: libp2pPubKey } = getKeyPairFromPeerId(remotePeer);
|
||||
|
||||
const handshake = new Handshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, undefined, xx);
|
||||
const handshake = new Handshake(false, libp2pPrivKey, libp2pPubKey, prologue, staticKeys, wrapped, localPeer, xx);
|
||||
|
||||
let receivedMessageBuffer = decodeMessageBuffer((await wrapped.readLP()).slice());
|
||||
// The first handshake message contains the initiator's ephemeral public key
|
||||
|
Reference in New Issue
Block a user