UNPKG

@libp2p/peer-record

Version:

Used to transfer signed peer data across the network

115 lines 4.41 kB
import { publicKeyFromProtobuf, publicKeyToProtobuf } from '@libp2p/crypto/keys'; import * as varint from 'uint8-varint'; import { Uint8ArrayList } from 'uint8arraylist'; import { equals as uint8ArrayEquals } from 'uint8arrays/equals'; import { fromString as uint8arraysFromString } from 'uint8arrays/from-string'; import { Envelope as Protobuf } from './envelope.js'; import { InvalidSignatureError } from './errors.js'; export class RecordEnvelope { /** * Unmarshal a serialized Envelope protobuf message */ static createFromProtobuf = async (data) => { const envelopeData = Protobuf.decode(data); const publicKey = publicKeyFromProtobuf(envelopeData.publicKey); return new RecordEnvelope({ publicKey, payloadType: envelopeData.payloadType, payload: envelopeData.payload, signature: envelopeData.signature }); }; /** * Seal marshals the given Record, places the marshaled bytes inside an Envelope * and signs it with the given peerId's private key */ static seal = async (record, privateKey) => { if (privateKey == null) { throw new Error('Missing private key'); } const domain = record.domain; const payloadType = record.codec; const payload = record.marshal(); const signData = formatSignaturePayload(domain, payloadType, payload); const signature = await privateKey.sign(signData.subarray()); return new RecordEnvelope({ publicKey: privateKey.publicKey, payloadType, payload, signature }); }; /** * Open and certify a given marshaled envelope. * Data is unmarshaled and the signature validated for the given domain. */ static openAndCertify = async (data, domain) => { const envelope = await RecordEnvelope.createFromProtobuf(data); const valid = await envelope.validate(domain); if (!valid) { throw new InvalidSignatureError('Envelope signature is not valid for the given domain'); } return envelope; }; publicKey; payloadType; payload; signature; marshaled; /** * The Envelope is responsible for keeping an arbitrary signed record * by a libp2p peer. */ constructor(init) { const { publicKey, payloadType, payload, signature } = init; this.publicKey = publicKey; this.payloadType = payloadType; this.payload = payload; this.signature = signature; } /** * Marshal the envelope content */ marshal() { if (this.marshaled == null) { this.marshaled = Protobuf.encode({ publicKey: publicKeyToProtobuf(this.publicKey), payloadType: this.payloadType, payload: this.payload.subarray(), signature: this.signature }); } return this.marshaled; } /** * Verifies if the other Envelope is identical to this one */ equals(other) { return uint8ArrayEquals(this.marshal(), other.marshal()); } /** * Validate envelope data signature for the given domain */ async validate(domain) { const signData = formatSignaturePayload(domain, this.payloadType, this.payload); return this.publicKey.verify(signData.subarray(), this.signature); } } /** * Helper function that prepares a Uint8Array to sign or verify a signature */ const formatSignaturePayload = (domain, payloadType, payload) => { // When signing, a peer will prepare a Uint8Array by concatenating the following: // - The length of the domain separation string string in bytes // - The domain separation string, encoded as UTF-8 // - The length of the payload_type field in bytes // - The value of the payload_type field // - The length of the payload field in bytes // - The value of the payload field const domainUint8Array = uint8arraysFromString(domain); const domainLength = varint.encode(domainUint8Array.byteLength); const payloadTypeLength = varint.encode(payloadType.length); const payloadLength = varint.encode(payload.length); return new Uint8ArrayList(domainLength, domainUint8Array, payloadTypeLength, payloadType, payloadLength, payload); }; //# sourceMappingURL=index.js.map