2019-11-20 21:38:14 +01:00
|
|
|
import { x25519, ed25519 } from 'bcrypto';
|
|
|
|
import protobuf from "protobufjs";
|
2019-11-22 12:52:59 +01:00
|
|
|
import { Buffer } from "buffer";
|
2019-11-27 08:39:06 +01:00
|
|
|
import debug from "debug";
|
2019-12-02 12:53:00 +01:00
|
|
|
import PeerId from "peer-id";
|
2019-11-11 21:58:04 +01:00
|
|
|
|
2019-11-20 13:23:36 +01:00
|
|
|
import { KeyPair } from "./@types/libp2p";
|
|
|
|
import { bytes } from "./@types/basic";
|
2019-11-22 12:52:59 +01:00
|
|
|
import { MessageBuffer } from "./xx";
|
2019-11-20 21:38:14 +01:00
|
|
|
|
2019-11-27 08:39:06 +01:00
|
|
|
export const logger = debug('libp2p:noise');
|
|
|
|
|
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-11-28 17:53:27 +01:00
|
|
|
export function encodeMessageBuffer(message: MessageBuffer): bytes {
|
2019-11-22 12:52:59 +01:00
|
|
|
return Buffer.concat([message.ne, message.ns, message.ciphertext]);
|
|
|
|
}
|
|
|
|
|
2019-11-28 17:53:27 +01:00
|
|
|
export function decodeMessageBuffer(message: bytes): MessageBuffer {
|
2019-11-22 12:52:59 +01:00
|
|
|
return {
|
|
|
|
ne: message.slice(0, 32),
|
2019-11-28 17:32:46 +01:00
|
|
|
ns: message.slice(32, 64),
|
|
|
|
ciphertext: message.slice(64, message.length),
|
2019-11-22 12:52:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 12:53:00 +01:00
|
|
|
export async function verifyPeerId(peerId: bytes, publicKey: bytes) {
|
|
|
|
const generatedPeerId = await PeerId.createFromPubKey(publicKey);
|
|
|
|
if (!generatedPeerId.equals(peerId)) {
|
|
|
|
Promise.reject("Peer ID doesn't match libp2p public key.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 13:18:31 +01:00
|
|
|
export async function verifySignedPayload(noiseStaticKey: bytes, plaintext: bytes, libp2pPublicKey: bytes) {
|
|
|
|
const NoiseHandshakePayload = await loadPayloadProto();
|
|
|
|
const receivedPayload = NoiseHandshakePayload.toObject(NoiseHandshakePayload.decode(plaintext));
|
2019-12-02 12:53:00 +01:00
|
|
|
const generatedPayload = getHandshakePayload(noiseStaticKey);
|
|
|
|
|
2019-12-02 13:18:31 +01:00
|
|
|
if (!ed25519.verify(generatedPayload, receivedPayload.noiseStaticKeySignature, libp2pPublicKey)) {
|
|
|
|
Promise.reject("Static key doesn't match to peer that signed payload!");
|
2019-12-02 12:53:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-27 14:19:35 +01:00
|
|
|
export const int16BEEncode = (value, target, offset) => {
|
|
|
|
target = target || Buffer.allocUnsafe(2);
|
|
|
|
return target.writeInt16BE(value, offset);
|
|
|
|
};
|
|
|
|
int16BEEncode.bytes = 2;
|
|
|
|
|
|
|
|
export const int16BEDecode = data => {
|
|
|
|
if (data.length < 2) throw RangeError('Could not decode int16BE');
|
2019-11-28 17:32:46 +01:00
|
|
|
return data.readInt16BE(0);
|
|
|
|
};
|
2019-11-27 14:19:35 +01:00
|
|
|
int16BEDecode.bytes = 2;
|