/* * Id is an object representation of a peer Id. a peer Id is a multihash */ 'use strict' const mh = require('multihashes') const cryptoKeys = require('libp2p-crypto/src/keys') const assert = require('assert') const withIs = require('class-is') class PeerId { constructor (id, privKey, pubKey) { assert(Buffer.isBuffer(id), 'invalid id provided') if (privKey && pubKey) { assert(privKey.public.bytes.equals(pubKey.bytes), 'inconsistent arguments') } this._id = id this._idB58String = mh.toB58String(this.id) this._privKey = privKey this._pubKey = pubKey } get id () { return this._id } set id (val) { throw new Error('Id is immutable') } get privKey () { return this._privKey } set privKey (privKey) { this._privKey = privKey } get pubKey () { if (this._pubKey) { return this._pubKey } if (this._privKey) { return this._privKey.public } } set pubKey (pubKey) { this._pubKey = pubKey } // Return the protobuf version of the public key, matching go ipfs formatting marshalPubKey () { if (this.pubKey) { return cryptoKeys.marshalPublicKey(this.pubKey) } } // Return the protobuf version of the private key, matching go ipfs formatting marshalPrivKey () { if (this.privKey) { return cryptoKeys.marshalPrivateKey(this.privKey) } } toPrint () { let pid = this.toB58String() // All sha256 nodes start with Qm // We can skip the Qm to make the peer.ID more useful if (pid.startsWith('Qm')) { pid = pid.slice(2) } let maxRunes = 6 if (pid.length < maxRunes) { maxRunes = pid.length } return '' } // return the jsonified version of the key, matching the formatting // of go-ipfs for its config file toJSON () { return { id: this.toB58String(), privKey: toB64Opt(this.marshalPrivKey()), pubKey: toB64Opt(this.marshalPubKey()) } } // encode/decode functions toHexString () { return mh.toHexString(this.id) } toBytes () { return this.id } toB58String () { return this._idB58String } isEqual (id) { if (Buffer.isBuffer(id)) { return this.id.equals(id) } else if (id.id) { return this.id.equals(id.id) } else { throw new Error('not valid Id') } } /* * Check if this PeerId instance is valid (privKey -> pubKey -> Id) */ isValid () { // TODO: needs better checking return Boolean(this.privKey && this.privKey.public && this.privKey.public.bytes && Buffer.isBuffer(this.pubKey.bytes) && this.privKey.public.bytes.equals(this.pubKey.bytes)) } } const PeerIdWithIs = withIs(PeerId, { className: 'PeerId', symbolName: '@libp2p/js-peer-id/PeerId' }) exports = module.exports = PeerIdWithIs // generation exports.create = async (opts) => { opts = opts || {} opts.bits = opts.bits || 2048 const key = await cryptoKeys.generateKeyPair('RSA', opts.bits) const digest = await key.public.hash() return new PeerIdWithIs(digest, key) } exports.createFromHexString = (str) => { return new PeerIdWithIs(mh.fromHexString(str)) } exports.createFromBytes = (buf) => { return new PeerIdWithIs(buf) } exports.createFromB58String = (str) => { return new PeerIdWithIs(mh.fromB58String(str)) } // Public Key input will be a buffer exports.createFromPubKey = async (key) => { let buf = key if (typeof buf === 'string') { buf = Buffer.from(key, 'base64') } if (!Buffer.isBuffer(buf)) { throw new Error('Supplied key is neither a base64 string nor a buffer') } const pubKey = await cryptoKeys.unmarshalPublicKey(buf) const digest = await pubKey.hash() return new PeerIdWithIs(digest, null, pubKey) } // Private key input will be a string exports.createFromPrivKey = async (key) => { let buf = key if (typeof buf === 'string') { buf = Buffer.from(key, 'base64') } if (!Buffer.isBuffer(buf)) { throw new Error('Supplied key is neither a base64 string nor a buffer') } const privKey = await cryptoKeys.unmarshalPrivateKey(buf) const digest = await privKey.public.hash() return new PeerIdWithIs(digest, privKey, privKey.public) } exports.createFromJSON = async (obj) => { let id = mh.fromB58String(obj.id) let rawPrivKey = obj.privKey && Buffer.from(obj.privKey, 'base64') let rawPubKey = obj.pubKey && Buffer.from(obj.pubKey, 'base64') let pub = rawPubKey && await cryptoKeys.unmarshalPublicKey(rawPubKey) if (!rawPrivKey) { return new PeerIdWithIs(id, null, pub) } const privKey = await cryptoKeys.unmarshalPrivateKey(rawPrivKey) const privDigest = await privKey.public.hash() let pubDigest if (pub) { pubDigest = await pub.hash() } if (pub && !privDigest.equals(pubDigest)) { throw new Error('Public and private key do not match') } if (id && !privDigest.equals(id)) { throw new Error('Id and private key do not match') } return new PeerIdWithIs(id, privKey, pub) } exports.isPeerId = (peerId) => { return Boolean(typeof peerId === 'object' && peerId._id && peerId._idB58String) } function toB64Opt (val) { if (val) { return val.toString('base64') } }