js-libp2p-noise/src/utils.ts

116 lines
3.5 KiB
TypeScript
Raw Normal View History

2020-02-17 09:18:44 +01:00
import {HKDF, SHA256, x25519} from 'bcrypto';
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';
2020-02-17 09:18:44 +01:00
import {KeyPair} from "./@types/libp2p";
2019-12-24 21:15:38 +01:00
import {bytes, bytes32} from "./@types/basic";
2020-02-10 13:38:35 +01:00
import {Hkdf, INoisePayload} from "./@types/handshake";
2020-02-17 09:18:44 +01:00
import {pb} from "./proto/payload";
2019-11-20 21:38:14 +01:00
2020-02-17 09:18:44 +01:00
const NoiseHandshakePayloadProto = 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,
}
}
2020-01-07 16:59:41 +01:00
export async function getPayload(
localPeer: PeerId,
staticPublicKey: bytes,
earlyData?: bytes,
): Promise<bytes> {
const signedPayload = await signPayload(localPeer, getHandshakePayload(staticPublicKey));
const earlyDataPayload = earlyData || Buffer.alloc(0);
2020-01-07 16:59:41 +01:00
return await createHandshakePayload(
localPeer.marshalPubKey(),
signedPayload,
earlyDataPayload
2020-01-07 16:59:41 +01:00
);
}
2019-11-20 21:38:14 +01:00
export async function createHandshakePayload(
2019-11-21 13:38:39 +01:00
libp2pPublicKey: bytes,
2019-11-20 21:38:14 +01:00
signedPayload: bytes,
earlyData?: bytes,
2019-11-28 17:53:27 +01:00
): Promise<bytes> {
2019-11-28 17:32:46 +01:00
2020-02-17 09:18:44 +01:00
const payloadInit = NoiseHandshakePayloadProto.create({
2020-02-05 22:10:51 +01:00
identityKey: libp2pPublicKey,
identitySig: signedPayload,
2020-02-17 09:18:44 +01:00
data: earlyData || null,
2019-11-20 21:38:14 +01:00
});
2020-02-17 09:18:44 +01:00
return Buffer.from(NoiseHandshakePayloadProto.encode(payloadInit).finish());
2019-11-20 21:38:14 +01:00
}
2020-01-07 16:59:41 +01:00
export async function signPayload(peerId: PeerId, payload: bytes): Promise<bytes> {
return peerId.privKey.sign(payload);
2019-11-11 21:58:04 +01:00
}
2019-11-20 21:38:14 +01:00
2020-02-17 09:18:44 +01:00
export async function getPeerIdFromPayload(payload: pb.INoiseHandshakePayload): Promise<PeerId> {
return await PeerId.createFromPubKey(Buffer.from(payload.identityKey as Uint8Array));
2020-02-07 20:21:27 +01:00
}
2020-03-11 09:43:40 +01:00
export async function decodePayload(payload: bytes|Uint8Array): Promise<pb.INoiseHandshakePayload> {
2020-02-17 09:18:44 +01:00
return NoiseHandshakePayloadProto.toObject(
2020-03-11 09:43:40 +01:00
NoiseHandshakePayloadProto.decode(Buffer.from(payload))
2020-02-10 13:38:35 +01:00
) as INoisePayload;
2020-02-07 20:21:27 +01:00
}
2020-02-14 10:10:42 +01:00
export function getHandshakePayload(publicKey: bytes): bytes {
return Buffer.concat([Buffer.from("noise-libp2p-static-key:"), publicKey]);
}
2019-11-28 17:32:46 +01:00
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
}
2020-02-08 12:23:35 +01:00
/**
* Verifies signed payload, throws on any irregularities.
2020-02-08 12:23:35 +01:00
* @param {bytes} noiseStaticKey - owner's noise static key
* @param {bytes} payload - decoded payload
* @param {PeerId} remotePeer - owner's libp2p peer ID
2020-02-08 12:23:35 +01:00
* @returns {Promise<PeerId>} - peer ID of payload owner
*/
export async function verifySignedPayload(
noiseStaticKey: bytes,
2020-02-17 09:18:44 +01:00
payload: pb.INoiseHandshakePayload,
remotePeer: PeerId
2020-02-08 12:23:35 +01:00
): Promise<PeerId> {
2020-03-11 09:43:40 +01:00
const identityKey = Buffer.from(payload.identityKey as Uint8Array);
if (!(await isValidPeerId(remotePeer.id, identityKey))) {
2019-12-03 13:39:33 +01:00
throw new Error("Peer ID doesn't match libp2p public key.");
}
const generatedPayload = getHandshakePayload(noiseStaticKey);
2020-01-07 17:08:08 +01:00
// Unmarshaling from PublicKey protobuf
2020-03-11 09:43:40 +01:00
const publicKey = crypto.keys.unmarshalPublicKey(identityKey);
if (!publicKey.verify(generatedPayload, payload.identitySig)) {
throw new Error("Static key doesn't match to peer that signed payload!");
2019-12-02 12:53:00 +01:00
}
2020-02-08 12:23:35 +01:00
return remotePeer;
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);
2020-02-17 09:18:44 +01:00
return [k1, k2, k3];
2019-12-24 21:15:38 +01:00
}
2019-12-29 18:23:43 +01:00
export function isValidPublicKey(pk: bytes): boolean {
2020-02-17 12:11:55 +01:00
return x25519.publicKeyVerify(pk.slice(0, 32));
2019-12-29 18:23:43 +01:00
}