UNPKG

ripplelib

Version:

A JavaScript API for interacting with Ripple in Node.js and the browser

268 lines (216 loc) 7.49 kB
'use strict'; /*eslint new-cap: 1*/ var sjcl = require('./utils').sjcl; var UInt160 = require('./uint160').UInt160; var UInt256 = require('./uint256').UInt256; var Base = require('./base').Base; var rfc1751 = require('./rfc1751'); function KeyPair() { this._curve = sjcl.ecc.curves.k256; this._secret = null; this._pubkey = null; } KeyPair.getRandom = function () { return this.from_bn_secret(sjcl.bn.fromBits(sjcl.random.randomWords(8, 6))); } KeyPair.from_json = function (j) { return j instanceof this ? j.clone() : new this().parse_json(j); }; KeyPair.from_bn_secret = function (j) { return j instanceof this ? j.clone() : new this().parse_bn_secret(j); }; KeyPair.is_valid = function (j) { return this.from_json(j).is_valid(); }; KeyPair.prototype.clone = function () { var c = new this.constructor(); if (this.is_valid()) { var exponent = sjcl.bn.fromBits(this._secret_bits()); c._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves.k256, exponent); } return c; }; KeyPair.prototype.parse_bn_secret = function (j) { this._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves.k256, j); return this; }; KeyPair.prototype.parse_json = function (j) { if (typeof j !== 'string') return this; var bn = undefined; if (/^[0-9a-fA-f]{64}$/.test(j)) { bn = new sjcl.bn(j, 16); } else if (/^([A-Z]{1,4} +)+[A-Z]{1,4}$/.test(j)) { try { var bits = sjcl.codec.bytes.toBits(rfc1751.decode(j)) bn = sjcl.bn.fromBits(bits); } catch (e) {}; } else { var versions = [ Base.VER_NODE_PRIVATE, Base.VER_ACCOUNT_PRIVATE, Base.VER_FAMILY_GENERATOR ]; bn = Base.decode_check(versions, j) || Base.decode_check(128, j, 'bitcoin'); // bitoin WIF key } if (bn) this.parse_bn_secret(bn); return this; }; KeyPair.prototype.is_valid = function () { return (this._secret instanceof sjcl.ecc.ecdsa.secretKey) && (this._secret._exponent instanceof sjcl.bn); }; /** * @private * * @return {sjcl.ecc.ecdsa.publicKey} public key */ KeyPair.prototype._pub = function () { var curve = this._curve; if (!this._pubkey && this._secret) { var exponent = this._secret._exponent; this._pubkey = new sjcl.ecc.ecdsa.publicKey(curve, curve.G.mult(exponent)); } return this._pubkey; }; /** * @private * * @return {sjcl.bitArray} private key bits */ KeyPair.prototype._secret_bits = function () { if (!this.is_valid()) { return null; } return this._secret.get(); }; /** * @private * * @return {sjcl.bitArray} public key bits in compressed form */ KeyPair.prototype._pub_bits = function () { var pub = this._pub(); if (!pub) { return null; } var point = pub._point, y_even = point.y.mod(2).equals(0); return sjcl.bitArray.concat([sjcl.bitArray.partial(8, y_even ? 0x02 : 0x03)], point.x.toBits(this._curve.r.bitLength())); }; /** * @return {String} public key bytes in compressed form, hex encoded. */ KeyPair.prototype.to_pub_hex = KeyPair.prototype.to_hex_pub = function () { var bits = this._pub_bits(); if (!bits) { return null; } return sjcl.codec.hex.fromBits(bits).toUpperCase(); }; function sha256_ripemd160(bits) { return sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits)); } KeyPair.prototype.get_address = function () { var bits = this._pub_bits(); if (!bits) { return null; } var hash = sha256_ripemd160(bits); var address = UInt160.from_bits(hash); address.set_version(Base.VER_ACCOUNT_ID); return address; }; KeyPair.prototype.to_address_string = function () { return this.get_address().to_json(); }; KeyPair.prototype.sign = function (hash) { var PARANOIA_256_BITS = 6; // sjcl constant for ensuring 256 bits of entropy hash = UInt256.from_json(hash); var sig = this._secret.sign(hash.to_bits(), PARANOIA_256_BITS); sig = this._secret.canonicalizeSignature(sig); return this._secret.encodeDER(sig); }; KeyPair.prototype.to_hex_pri = KeyPair.prototype.to_pri_hex = function () { var bits = this._secret_bits(); if (!bits) return null; return sjcl.codec.hex.fromBits(bits).toUpperCase(); }; KeyPair.prototype.to_pri_node = function () { var bits = this._secret_bits(); if (!bits) return null; return Base.encode_check(Base.VER_NODE_PRIVATE, sjcl.codec.bytes.fromBits(bits)); }; KeyPair.prototype.to_pub_node = function () { var bits = this._pub_bits(); if (!bits) return null; return Base.encode_check(Base.VER_NODE_PUBLIC, sjcl.codec.bytes.fromBits(bits)); }; KeyPair.prototype.to_pri_generator = function () { var bits = this._secret_bits(); if (!bits) return null; return Base.encode_check(Base.VER_FAMILY_GENERATOR, sjcl.codec.bytes.fromBits(bits)); }; KeyPair.prototype.to_pub_generator = function () { var bits = this._pub_bits(); if (!bits) return null; return Base.encode_check(Base.VER_FAMILY_GENERATOR, sjcl.codec.bytes.fromBits(bits)); }; KeyPair.prototype.to_wif = KeyPair.prototype.to_pri_string = KeyPair.prototype.to_pri_account = function () { var bits = this._secret_bits(); if (!bits) return null; return Base.encode_check(Base.VER_ACCOUNT_PRIVATE, sjcl.codec.bytes.fromBits(bits)); }; KeyPair.prototype.to_pub_string = KeyPair.prototype.to_pub_account = function () { var bits = this._pub_bits(); if (!bits) return null; return Base.encode_check(Base.VER_ACCOUNT_PUBLIC, sjcl.codec.bytes.fromBits(bits)); }; KeyPair.prototype.to_wif_bitcoin = function () { var bits = this._secret_bits(); if (!bits) return null; return Base.encode_check(128, sjcl.codec.bytes.fromBits(bits), 'bitcoin'); }; KeyPair.prototype.to_human = KeyPair.prototype.to_rfc1751 = function () { var bits = this._secret_bits(); if (!bits) return null; return rfc1751.encode(sjcl.codec.bytes.fromBits(bits)); }; // get child-KeyPair, similar to the concept of bitcoin BIP-0032. // when index > 2^31, look for hardened-child (offset derived from privateKey). KeyPair.prototype.get_child = function (index, forceHardened) { if (typeof index == 'undefined') index = 0; var isValidUInt32 = typeof index === 'number' && index >= 0 && index < Math.pow(2, 32); if (!isValidUInt32) { throw new Error('childkey index must be a valid UInt32'); } var hardened_value = Math.pow(2, 31); if (forceHardened && index < hardened_value) index += hardened_value; var isHardened = index >= hardened_value; var curve = this._curve; var private_gen = this._secret._exponent; var public_gen = curve.G.mult(private_gen); var chainCode = undefined; var chainSeed = isHardened ? sjcl.codec.bytes.fromBits(private_gen.toBits()) : public_gen.toBytesCompressed(); var i = 0; do { chainCode = sjcl.bn.fromBits(firstHalfOfSHA512(append_int(append_int(chainSeed, index), i))); i++; } while (!curve.r.greaterEquals(chainCode)); var sec = chainCode.add(private_gen).mod(curve.r); return KeyPair.from_bn_secret(sec); }; function append_int(a, i) { return [].concat(a, i >> 24, i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); } function firstHalfOfSHA512(bytes) { return sjcl.bitArray.bitSlice(sjcl.hash.sha512.hash(sjcl.codec.bytes.toBits(bytes)), 0, 256); } exports.KeyPair = KeyPair;