Files
js-libp2p-noise/src/utils.ts
achingbrain 1a6490d829 chore: update deps to latest versions
I've tried to make the minimum amount of changes necessary for this,
since the underlying crypto libraries only support node Buffers or
BufferLists there doesn't seem a lot of point in doing lots of
conversions between Uint8Arrays and Buffers.

BREAKING CHANGES:

- All deps use Uint8Arrays in place of node Buffers
2020-08-11 11:41:10 +01:00

117 lines
3.7 KiB
TypeScript

import HKDF from 'bcrypto/lib/hkdf'
import x25519 from 'bcrypto/lib/js/x25519'
import SHA256 from 'bcrypto/lib/js/sha256'
import { Buffer } from 'buffer'
import PeerId from 'peer-id'
import { keys } from 'libp2p-crypto'
import { KeyPair } from './@types/libp2p'
import { bytes, bytes32 } from './@types/basic'
import { Hkdf, INoisePayload } from './@types/handshake'
import { pb } from './proto/payload'
import uint8ArrayEquals from 'uint8arrays/equals'
const NoiseHandshakePayloadProto = pb.NoiseHandshakePayload
export function generateKeypair (): KeyPair {
const privateKey = x25519.privateKeyGenerate()
const publicKey = x25519.publicKeyCreate(privateKey)
return {
publicKey,
privateKey
}
}
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)
return await createHandshakePayload(
localPeer.marshalPubKey(),
signedPayload,
earlyDataPayload
)
}
export function createHandshakePayload (
libp2pPublicKey: Uint8Array,
signedPayload: Uint8Array,
earlyData?: Uint8Array
): bytes {
const payloadInit = NoiseHandshakePayloadProto.create({
identityKey: Buffer.from(libp2pPublicKey),
identitySig: signedPayload,
data: earlyData || null
})
return Buffer.from(NoiseHandshakePayloadProto.encode(payloadInit).finish())
}
export async function signPayload (peerId: PeerId, payload: bytes): Promise<bytes> {
return Buffer.from(await peerId.privKey.sign(payload))
}
export async function getPeerIdFromPayload (payload: pb.INoiseHandshakePayload): Promise<PeerId> {
return await PeerId.createFromPubKey(Buffer.from(payload.identityKey as Uint8Array))
}
export function decodePayload (payload: bytes|Uint8Array): pb.INoiseHandshakePayload {
return NoiseHandshakePayloadProto.toObject(
NoiseHandshakePayloadProto.decode(Buffer.from(payload))
) as INoisePayload
}
export function getHandshakePayload (publicKey: bytes): bytes {
return Buffer.concat([Buffer.from('noise-libp2p-static-key:'), publicKey])
}
async function isValidPeerId (peerId: Uint8Array, publicKeyProtobuf: bytes) {
const generatedPeerId = await PeerId.createFromPubKey(publicKeyProtobuf)
return uint8ArrayEquals(generatedPeerId.id, peerId)
}
/**
* Verifies signed payload, throws on any irregularities.
* @param {bytes} noiseStaticKey - owner's noise static key
* @param {bytes} payload - decoded payload
* @param {PeerId} remotePeer - owner's libp2p peer ID
* @returns {Promise<PeerId>} - peer ID of payload owner
*/
export async function verifySignedPayload (
noiseStaticKey: bytes,
payload: pb.INoiseHandshakePayload,
remotePeer: PeerId
): Promise<PeerId> {
const identityKey = Buffer.from(payload.identityKey as Uint8Array)
if (!(await isValidPeerId(remotePeer.id, identityKey))) {
throw new Error("Peer ID doesn't match libp2p public key.")
}
const generatedPayload = getHandshakePayload(noiseStaticKey)
// Unmarshaling from PublicKey protobuf
const publicKey = keys.unmarshalPublicKey(identityKey)
if (!payload.identitySig || !publicKey.verify(generatedPayload, Buffer.from(payload.identitySig))) {
throw new Error("Static key doesn't match to peer that signed payload!")
}
return PeerId.createFromPubKey(identityKey)
}
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]
}
export function isValidPublicKey (pk: bytes): boolean {
return x25519.publicKeyVerify(pk.slice(0, 32))
}