diff --git a/src/handshake-ik.ts b/src/handshake-ik.ts index 9ce370a..76d9e6a 100644 --- a/src/handshake-ik.ts +++ b/src/handshake-ik.ts @@ -5,6 +5,8 @@ import {bytes, bytes32} from "./@types/basic"; import {KeyPair, PeerId} from "./@types/libp2p"; import {IHandshake} from "./@types/handshake-interface"; import {Buffer} from "buffer"; +import {decode0, decode1, encode0, encode1} from "./encoder"; +import {verifySignedPayload} from "./utils"; export class IKHandshake implements IHandshake { public isInitiator: boolean; @@ -38,6 +40,38 @@ export class IKHandshake implements IHandshake { this.session = this.ik.initSession(this.isInitiator, this.prologue, this.staticKeypair, remoteStaticKey); } + public async stage0(): Promise { + if (this.isInitiator) { + const messageBuffer = this.ik.sendMessage(this.session, this.payload); + this.connection.writeLP(encode0(messageBuffer)); + } else { + const receivedMessageBuffer = decode0(await this.connection.readLP()); + const plaintext = this.ik.recvMessage(this.session, receivedMessageBuffer); + + try { + await verifySignedPayload(receivedMessageBuffer.ns, plaintext, this.remotePeer.id); + } catch (e) { + throw new Error(`Error occurred while verifying signed payload: ${e.message}`); + } + } + } + + public async stage1(): Promise { + if (this.isInitiator) { + const receivedMessageBuffer = decode1(await this.connection.readLP()); + const plaintext = this.ik.recvMessage(this.session, receivedMessageBuffer); + + try { + await verifySignedPayload(receivedMessageBuffer.ns, plaintext, this.remotePeer.id); + } catch (e) { + throw new Error(`Error occurred while verifying signed payload: ${e.message}`); + } + } else { + const messageBuffer = this.ik.sendMessage(this.session, this.payload); + this.connection.writeLP(encode1(messageBuffer)); + } + } + public decrypt(ciphertext: Buffer, session: NoiseSession): Buffer { const cs = this.getCS(session, false); return this.ik.decryptWithAd(cs, Buffer.alloc(0), ciphertext); diff --git a/src/noise.ts b/src/noise.ts index cebdec9..752e2a7 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -179,7 +179,9 @@ export class Noise implements INoiseConnection { handshake: IKHandshake, payload: bytes, ): Promise { - // TODO + + await handshake.stage0(); + await handshake.stage1(); return handshake; } diff --git a/test/ik-handshake.test.ts b/test/ik-handshake.test.ts new file mode 100644 index 0000000..1201d3b --- /dev/null +++ b/test/ik-handshake.test.ts @@ -0,0 +1,56 @@ +import Wrap from "it-pb-rpc"; +import Duplex from 'it-pair/duplex'; +import {Buffer} from "buffer"; + +import {createPeerIdsFromFixtures} from "./fixtures/peer"; +import {generateKeypair, getPayload} from "../src/utils"; +import {IKHandshake} from "../src/handshake-ik"; +import {assert} from "chai"; + +describe("IK Handshake", () => { + let peerA, peerB, fakePeer; + + before(async () => { + [peerA, peerB, fakePeer] = await createPeerIdsFromFixtures(3); + }); + + it("should finish both stages as initiator and responder", async() => { + try { + const duplex = Duplex(); + const connectionFrom = Wrap(duplex[0]); + const connectionTo = Wrap(duplex[1]); + + const prologue = Buffer.from('/noise'); + const staticKeysInitiator = generateKeypair(); + const staticKeysResponder = generateKeypair(); + + const initPayload = await getPayload(peerA, staticKeysInitiator.publicKey); + const handshakeInit = new IKHandshake(true, initPayload, prologue, staticKeysInitiator, connectionFrom, peerB, staticKeysResponder.publicKey); + + const respPayload = await getPayload(peerB, staticKeysResponder.publicKey); + const handshakeResp = new IKHandshake(false, respPayload, prologue, staticKeysResponder, connectionTo, peerA, staticKeysInitiator.publicKey); + + await handshakeInit.stage0(); + await handshakeResp.stage0(); + + await handshakeResp.stage1(); + await handshakeInit.stage1(); + + // Test shared key + if (handshakeInit.session.cs1 && handshakeResp.session.cs1 && handshakeInit.session.cs2 && handshakeResp.session.cs2) { + assert(handshakeInit.session.cs1.k.equals(handshakeResp.session.cs1.k)); + assert(handshakeInit.session.cs2.k.equals(handshakeResp.session.cs2.k)); + } else { + assert(false); + } + + // Test encryption and decryption + const encrypted = handshakeInit.encrypt(Buffer.from("encryptthis"), handshakeInit.session); + const decrypted = handshakeResp.decrypt(encrypted, handshakeResp.session); + assert(decrypted.equals(Buffer.from("encryptthis"))); + } catch (e) { + console.error(e); + assert(false, e.message); + } + }); +});