cipher-ethereum
Version:
An Ethereum library used by Cipher Browser, a mobile Ethereum client
310 lines (298 loc) • 11.3 kB
JavaScript
'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();
}