js-libp2p-noise/src/utils.ts

108 lines
3.4 KiB
TypeScript
Raw Normal View History

2019-12-24 21:15:38 +01:00
import { x25519, ed25519, HKDF, SHA256 } from 'bcrypto';
2019-11-20 21:38:14 +01:00
import protobuf from "protobufjs";
2019-11-22 12:52:59 +01:00
import { Buffer } from "buffer";
2019-12-02 12:53:00 +01:00
import PeerId from "peer-id";
2019-12-03 13:39:33 +01:00
import * as crypto from 'libp2p-crypto';
2019-11-11 21:58:04 +01:00
2019-11-20 13:23:36 +01:00
import { KeyPair } from "./@types/libp2p";
2019-12-24 21:15:38 +01:00
import {bytes, bytes32} from "./@types/basic";
import {Hkdf} from "./@types/handshake";
2019-11-20 21:38:14 +01:00
export async function loadPayloadProto () {
const payloadProtoBuf = await protobuf.load("protos/payload.proto");
return payloadProtoBuf.lookupType("pb.NoiseHandshakePayload");
}
2019-11-11 21:58:04 +01:00
2019-11-28 17:53:27 +01:00
export function generateKeypair(): KeyPair {
2019-11-11 21:58:04 +01:00
const privateKey = x25519.privateKeyGenerate();
const publicKey = x25519.publicKeyCreate(privateKey);
return {
publicKey,
privateKey,
}
}
2019-11-20 21:38:14 +01:00
export async function createHandshakePayload(
2019-11-21 13:38:39 +01:00
libp2pPublicKey: bytes,
2019-11-28 17:32:46 +01:00
libp2pPrivateKey: bytes,
2019-11-20 21:38:14 +01:00
signedPayload: bytes,
2019-11-28 17:32:46 +01:00
signedEarlyData?: EarlyDataPayload,
2019-11-28 17:53:27 +01:00
): Promise<bytes> {
2019-11-20 21:38:14 +01:00
const NoiseHandshakePayload = await loadPayloadProto();
2019-11-28 17:32:46 +01:00
const earlyDataPayload = signedEarlyData ?
{
libp2pData: signedEarlyData.libp2pData,
libp2pDataSignature: signedEarlyData.libp2pDataSignature,
} : {};
2019-11-20 21:38:14 +01:00
const payloadInit = NoiseHandshakePayload.create({
2019-11-21 13:38:39 +01:00
libp2pKey: libp2pPublicKey,
2019-11-20 21:38:14 +01:00
noiseStaticKeySignature: signedPayload,
2019-11-28 17:32:46 +01:00
...earlyDataPayload,
2019-11-20 21:38:14 +01:00
});
return Buffer.from(NoiseHandshakePayload.encode(payloadInit).finish());
}
2019-11-28 17:32:46 +01:00
export function signPayload(libp2pPrivateKey: bytes, payload: bytes) {
return ed25519.sign(payload, libp2pPrivateKey);
2019-11-11 21:58:04 +01:00
}
2019-11-20 21:38:14 +01:00
2019-11-28 17:32:46 +01:00
type EarlyDataPayload = {
libp2pData: bytes;
libp2pDataSignature: bytes;
}
2019-11-20 21:38:14 +01:00
2019-11-28 17:53:27 +01:00
export function signEarlyDataPayload(libp2pPrivateKey: bytes, earlyData: bytes): EarlyDataPayload {
2019-11-20 21:38:14 +01:00
const payload = getEarlyDataPayload(earlyData);
2019-11-28 17:32:46 +01:00
const signedPayload = signPayload(libp2pPrivateKey, payload);
2019-11-20 21:38:14 +01:00
return {
libp2pData: payload,
libp2pDataSignature: signedPayload,
}
}
2019-11-28 17:32:46 +01:00
export const getHandshakePayload = (publicKey: bytes ) => Buffer.concat([Buffer.from("noise-libp2p-static-key:"), publicKey]);
export const getEarlyDataPayload = (earlyData: bytes) => Buffer.concat([Buffer.from("noise-libp2p-early-data:"), earlyData]);
2019-12-03 13:39:33 +01:00
async function isValidPeerId(peerId: bytes, publicKeyProtobuf: bytes) {
const generatedPeerId = await PeerId.createFromPubKey(publicKeyProtobuf);
return generatedPeerId.id.equals(peerId);
2019-12-02 12:53:00 +01:00
}
2019-12-03 13:39:33 +01:00
export async function verifySignedPayload(noiseStaticKey: bytes, plaintext: bytes, peerId: bytes) {
2019-12-02 13:18:31 +01:00
const NoiseHandshakePayload = await loadPayloadProto();
const receivedPayload = NoiseHandshakePayload.toObject(NoiseHandshakePayload.decode(plaintext));
2019-12-02 12:53:00 +01:00
2019-12-03 13:39:33 +01:00
if (!(await isValidPeerId(peerId, receivedPayload.libp2pKey)) ) {
throw new Error("Peer ID doesn't match libp2p public key.");
}
const generatedPayload = getHandshakePayload(noiseStaticKey);
// Unmarshaling from PublicKey protobuf and taking key buffer only.
const publicKey = crypto.keys.unmarshalPublicKey(receivedPayload.libp2pKey).marshal();
if (!ed25519.verify(generatedPayload, receivedPayload.noiseStaticKeySignature, publicKey)) {
throw new Error("Static key doesn't match to peer that signed payload!");
2019-12-02 12:53:00 +01:00
}
}
2019-12-24 21:15:38 +01:00
export function getHkdf(ck: bytes32, ikm: bytes): Hkdf {
const info = Buffer.alloc(0);
const prk = HKDF.extract(SHA256, ikm, ck);
const okm = HKDF.expand(SHA256, prk, info, 96);
const k1 = okm.slice(0, 32);
const k2 = okm.slice(32, 64);
const k3 = okm.slice(64, 96);
return [ k1, k2, k3 ];
}
2019-12-29 18:23:43 +01:00
export function isValidPublicKey(pk: bytes): boolean {
return x25519.publicKeyVerify(pk);
}