UNPKG

cipher-ethereum

Version:

An Ethereum library used by Cipher Browser, a mobile Ethereum client

310 lines (298 loc) 11.3 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.HDKey = exports.HDKEY_VERSIONS = undefined; var _bs = require('bs58'); var bs58 = _interopRequireWildcard(_bs); var _crypto = require('crypto'); var crypto = _interopRequireWildcard(_crypto); var _bn = require('bn.js'); var _bn2 = _interopRequireDefault(_bn); var _elliptic = require('elliptic'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } var HARDENED_KEY_OFFSET = 0x80000000; var secp256k1 = new _elliptic.ec('secp256k1'); var HDKEY_VERSIONS = exports.HDKEY_VERSIONS = { bitcoinMain: { bip32: { public: 0x0488b21e, private: 0x0488ade4 }, public: 0 }, bitcoinTest: { bip32: { public: 0x043587cf, private: 0x04358394 }, public: 0 } }; var HDKey = /** @class */function () { function HDKey(_a) { var privateKey = _a.privateKey, publicKey = _a.publicKey, chainCode = _a.chainCode, index = _a.index, depth = _a.depth, parentFingerprint = _a.parentFingerprint, version = _a.version; if (!privateKey && !publicKey) { throw new Error('either private key or public key must be provided'); } if (privateKey) { this._privateKey = privateKey; var ecdh = crypto.createECDH('secp256k1'); if (ecdh.curve && ecdh.curve.keyFromPrivate) { // ECDH is not native, fallback to pure-JS elliptic lib this._publicKey = Buffer.from(secp256k1.keyFromPrivate(privateKey).getPublic(true, 'hex'), 'hex'); } else { ecdh.setPrivateKey(privateKey); this._publicKey = Buffer.from(ecdh.getPublicKey('hex', 'compressed'), 'hex'); } } else if (publicKey) { this._publicKey = publicKey; } this._chainCode = chainCode; this._depth = depth || 0; this._index = index || 0; this._parentFingerprint = parentFingerprint; this._keyIdentifier = hash160(this._publicKey); this._version = version || HDKEY_VERSIONS.bitcoinMain; } HDKey.parseMasterSeed = function (seed, version) { var i = hmacSha512('Bitcoin seed', seed); var iL = i.slice(0, 32); var iR = i.slice(32); return new HDKey({ privateKey: iL, chainCode: iR, version: version }); }; HDKey.parseExtendedKey = function (key, version) { if (version === void 0) { version = HDKEY_VERSIONS.bitcoinMain; } // version_bytes[4] || depth[1] || parent_fingerprint[4] || index[4] || chain_code[32] || key_data[33] || checksum[4] var decoded = Buffer.from(bs58.decode(key)); if (decoded.length > 112) { throw new Error('invalid extended key'); } var checksum = decoded.slice(-4); var buf = decoded.slice(0, -4); if (!sha256(sha256(buf)).slice(0, 4).equals(checksum)) { throw new Error('invalid checksum'); } var o = 0; var versionRead = buf.readUInt32BE(o); o += 4; var depth = buf.readUInt8(o); o += 1; var parentFingerprint = buf.slice(o, o += 4); if (parentFingerprint.readUInt32BE(0) === 0) { parentFingerprint = undefined; } var index = buf.readUInt32BE(o); o += 4; var chainCode = buf.slice(o, o += 32); var keyData = buf.slice(o); var privateKey = keyData[0] === 0 ? keyData.slice(1) : undefined; var publicKey = keyData[0] !== 0 ? keyData : undefined; if (privateKey && versionRead !== version.bip32.private || publicKey && versionRead !== version.bip32.public) { throw new Error('invalid version bytes'); } return new HDKey({ privateKey: privateKey, publicKey: publicKey, chainCode: chainCode, index: index, depth: depth, parentFingerprint: parentFingerprint, version: version }); }; Object.defineProperty(HDKey.prototype, "privateKey", { get: function () { return this._privateKey || null; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "publicKey", { get: function () { return this._publicKey; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "chainCode", { get: function () { return this._chainCode; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "depth", { get: function () { return this._depth; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "parentFingerprint", { get: function () { return this._parentFingerprint || null; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "index", { get: function () { return this._index; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "keyIdentifier", { get: function () { return this._keyIdentifier; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "fingerprint", { get: function () { return this._keyIdentifier.slice(0, 4); }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "version", { get: function () { return this._version; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "extendedPrivateKey", { get: function () { return this._privateKey ? this.serialize(this._version.bip32.private, this._privateKey) : null; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "extendedPublicKey", { get: function () { return this.serialize(this._version.bip32.public, this._publicKey); }, enumerable: false, configurable: true }); HDKey.prototype.derive = function (chain) { var c = chain.toLowerCase(); var childKey = this; c.split('/').forEach(function (path) { var p = path.trim(); if (p === 'm' || p === "m'" || p === '') { return; } var index = Number.parseInt(p, 10); if (Number.isNaN(index)) { throw new Error('invalid child key derivation chain'); } var hardened = p.slice(-1) === "'"; childKey = childKey.deriveChildKey(index, hardened); }); return childKey; }; HDKey.prototype.deriveChildKey = function (childIndex, hardened) { if (childIndex >= HARDENED_KEY_OFFSET) { throw new Error('invalid index'); } if (!this.privateKey && !this.publicKey) { throw new Error('either private key or public key must be provided'); } var index = childIndex; var data = Buffer.alloc(37); var o = 0; if (hardened) { if (!this.privateKey) { throw new Error('cannot derive a hardened child key from a public key'); } // 0x00 || ser256(kpar) || ser32(i) // 0x00[1] || parent_private_key[32] || child_index[4] index += HARDENED_KEY_OFFSET; o += 1; o += this.privateKey.copy(data, o); } else { // serP(point(kpar)) || ser32(i) // compressed_parent_public_key[33] || child_index[4] o += this.publicKey.copy(data, o); } o += data.writeUInt32BE(index, o); var i = hmacSha512(this.chainCode, data); var iL = new _bn2.default(i.slice(0, 32)); var iR = i.slice(32); // if parse256(IL) >= n, the resulting key is invalid; proceed with the next value for i if (iL.cmp(secp256k1.n) >= 0) { return this.deriveChildKey(childIndex + 1, hardened); } if (this.privateKey) { // ki is parse256(IL) + kpar (mod n) var childKey = iL.add(new _bn2.default(this.privateKey)).mod(secp256k1.n); // if ki = 0, the resulting key is invalid; proceed with the next value for i if (childKey.cmp(new _bn2.default(0)) === 0) { return this.deriveChildKey(childIndex + 1, hardened); } return new HDKey({ depth: this.depth + 1, privateKey: childKey.toArrayLike(Buffer, 'be', 32), chainCode: iR, parentFingerprint: this.fingerprint, index: index, version: this.version }); } else { // Ki is point(parse256(IL)) + Kpar = G * IL + Kpar var parentKey = secp256k1.keyFromPublic(this.publicKey).pub; var childKey = secp256k1.g.mul(iL).add(parentKey); // if Ki is the point at infinity, the resulting key is invalid; proceed with the next value for i if (childKey.isInfinity()) { return this.deriveChildKey(childIndex + 1, false); } var compressedChildKey = Buffer.from(childKey.encode(null, true)); return new HDKey({ depth: this.depth + 1, publicKey: compressedChildKey, chainCode: iR, parentFingerprint: this.fingerprint, index: index, version: this.version }); } }; HDKey.prototype.serialize = function (version, key) { // version_bytes[4] || depth[1] || parent_fingerprint[4] || index[4] || chain_code[32] || key_data[33] || checksum[4] var buf = Buffer.alloc(78); var o = buf.writeUInt32BE(version, 0); o = buf.writeUInt8(this.depth, o); o += this.parentFingerprint ? this.parentFingerprint.copy(buf, o) : 4; o = buf.writeUInt32BE(this.index, o); o += this.chainCode.copy(buf, o); o += 33 - key.length; key.copy(buf, o); var checksum = sha256(sha256(buf)).slice(0, 4); return bs58.encode(Buffer.concat([buf, checksum])); }; return HDKey; }(); exports.HDKey = HDKey; function hmacSha512(key, data) { return crypto.createHmac('sha512', key).update(data).digest(); } function sha256(data) { return crypto.createHash('sha256').update(data).digest(); } function hash160(data) { var d = crypto.createHash('sha256').update(data).digest(); return crypto.createHash('rmd160').update(d).digest(); }