@libp2p/peer-record
Version:
Used to transfer signed peer data across the network
115 lines • 4.41 kB
JavaScript
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