index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.HDKey = exports.HARDENED_OFFSET = void 0;
  4. /*! scure-bip32 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) */
  5. const hmac_1 = require("@noble/hashes/hmac");
  6. const ripemd160_1 = require("@noble/hashes/ripemd160");
  7. const sha256_1 = require("@noble/hashes/sha256");
  8. const sha512_1 = require("@noble/hashes/sha512");
  9. const _assert_1 = require("@noble/hashes/_assert");
  10. const utils_1 = require("@noble/hashes/utils");
  11. const secp256k1_1 = require("@noble/curves/secp256k1");
  12. const modular_1 = require("@noble/curves/abstract/modular");
  13. const base_1 = require("@scure/base");
  14. const Point = secp256k1_1.secp256k1.ProjectivePoint;
  15. const base58check = (0, base_1.createBase58check)(sha256_1.sha256);
  16. function bytesToNumber(bytes) {
  17. return BigInt(`0x${(0, utils_1.bytesToHex)(bytes)}`);
  18. }
  19. function numberToBytes(num) {
  20. return (0, utils_1.hexToBytes)(num.toString(16).padStart(64, '0'));
  21. }
  22. const MASTER_SECRET = (0, utils_1.utf8ToBytes)('Bitcoin seed');
  23. // Bitcoin hardcoded by default
  24. const BITCOIN_VERSIONS = { private: 0x0488ade4, public: 0x0488b21e };
  25. exports.HARDENED_OFFSET = 0x80000000;
  26. const hash160 = (data) => (0, ripemd160_1.ripemd160)((0, sha256_1.sha256)(data));
  27. const fromU32 = (data) => (0, utils_1.createView)(data).getUint32(0, false);
  28. const toU32 = (n) => {
  29. if (!Number.isSafeInteger(n) || n < 0 || n > 2 ** 32 - 1) {
  30. throw new Error(`Invalid number=${n}. Should be from 0 to 2 ** 32 - 1`);
  31. }
  32. const buf = new Uint8Array(4);
  33. (0, utils_1.createView)(buf).setUint32(0, n, false);
  34. return buf;
  35. };
  36. class HDKey {
  37. get fingerprint() {
  38. if (!this.pubHash) {
  39. throw new Error('No publicKey set!');
  40. }
  41. return fromU32(this.pubHash);
  42. }
  43. get identifier() {
  44. return this.pubHash;
  45. }
  46. get pubKeyHash() {
  47. return this.pubHash;
  48. }
  49. get privateKey() {
  50. return this.privKeyBytes || null;
  51. }
  52. get publicKey() {
  53. return this.pubKey || null;
  54. }
  55. get privateExtendedKey() {
  56. const priv = this.privateKey;
  57. if (!priv) {
  58. throw new Error('No private key');
  59. }
  60. return base58check.encode(this.serialize(this.versions.private, (0, utils_1.concatBytes)(new Uint8Array([0]), priv)));
  61. }
  62. get publicExtendedKey() {
  63. if (!this.pubKey) {
  64. throw new Error('No public key');
  65. }
  66. return base58check.encode(this.serialize(this.versions.public, this.pubKey));
  67. }
  68. static fromMasterSeed(seed, versions = BITCOIN_VERSIONS) {
  69. (0, _assert_1.bytes)(seed);
  70. if (8 * seed.length < 128 || 8 * seed.length > 512) {
  71. throw new Error(`HDKey: wrong seed length=${seed.length}. Should be between 128 and 512 bits; 256 bits is advised)`);
  72. }
  73. const I = (0, hmac_1.hmac)(sha512_1.sha512, MASTER_SECRET, seed);
  74. return new HDKey({
  75. versions,
  76. chainCode: I.slice(32),
  77. privateKey: I.slice(0, 32),
  78. });
  79. }
  80. static fromExtendedKey(base58key, versions = BITCOIN_VERSIONS) {
  81. // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33)
  82. const keyBuffer = base58check.decode(base58key);
  83. const keyView = (0, utils_1.createView)(keyBuffer);
  84. const version = keyView.getUint32(0, false);
  85. const opt = {
  86. versions,
  87. depth: keyBuffer[4],
  88. parentFingerprint: keyView.getUint32(5, false),
  89. index: keyView.getUint32(9, false),
  90. chainCode: keyBuffer.slice(13, 45),
  91. };
  92. const key = keyBuffer.slice(45);
  93. const isPriv = key[0] === 0;
  94. if (version !== versions[isPriv ? 'private' : 'public']) {
  95. throw new Error('Version mismatch');
  96. }
  97. if (isPriv) {
  98. return new HDKey({ ...opt, privateKey: key.slice(1) });
  99. }
  100. else {
  101. return new HDKey({ ...opt, publicKey: key });
  102. }
  103. }
  104. static fromJSON(json) {
  105. return HDKey.fromExtendedKey(json.xpriv);
  106. }
  107. constructor(opt) {
  108. this.depth = 0;
  109. this.index = 0;
  110. this.chainCode = null;
  111. this.parentFingerprint = 0;
  112. if (!opt || typeof opt !== 'object') {
  113. throw new Error('HDKey.constructor must not be called directly');
  114. }
  115. this.versions = opt.versions || BITCOIN_VERSIONS;
  116. this.depth = opt.depth || 0;
  117. this.chainCode = opt.chainCode || null;
  118. this.index = opt.index || 0;
  119. this.parentFingerprint = opt.parentFingerprint || 0;
  120. if (!this.depth) {
  121. if (this.parentFingerprint || this.index) {
  122. throw new Error('HDKey: zero depth with non-zero index/parent fingerprint');
  123. }
  124. }
  125. if (opt.publicKey && opt.privateKey) {
  126. throw new Error('HDKey: publicKey and privateKey at same time.');
  127. }
  128. if (opt.privateKey) {
  129. if (!secp256k1_1.secp256k1.utils.isValidPrivateKey(opt.privateKey)) {
  130. throw new Error('Invalid private key');
  131. }
  132. this.privKey =
  133. typeof opt.privateKey === 'bigint' ? opt.privateKey : bytesToNumber(opt.privateKey);
  134. this.privKeyBytes = numberToBytes(this.privKey);
  135. this.pubKey = secp256k1_1.secp256k1.getPublicKey(opt.privateKey, true);
  136. }
  137. else if (opt.publicKey) {
  138. this.pubKey = Point.fromHex(opt.publicKey).toRawBytes(true); // force compressed point
  139. }
  140. else {
  141. throw new Error('HDKey: no public or private key provided');
  142. }
  143. this.pubHash = hash160(this.pubKey);
  144. }
  145. derive(path) {
  146. if (!/^[mM]'?/.test(path)) {
  147. throw new Error('Path must start with "m" or "M"');
  148. }
  149. if (/^[mM]'?$/.test(path)) {
  150. return this;
  151. }
  152. const parts = path.replace(/^[mM]'?\//, '').split('/');
  153. // tslint:disable-next-line
  154. let child = this;
  155. for (const c of parts) {
  156. const m = /^(\d+)('?)$/.exec(c);
  157. const m1 = m && m[1];
  158. if (!m || m.length !== 3 || typeof m1 !== 'string') {
  159. throw new Error(`Invalid child index: ${c}`);
  160. }
  161. let idx = +m1;
  162. if (!Number.isSafeInteger(idx) || idx >= exports.HARDENED_OFFSET) {
  163. throw new Error('Invalid index');
  164. }
  165. // hardened key
  166. if (m[2] === "'") {
  167. idx += exports.HARDENED_OFFSET;
  168. }
  169. child = child.deriveChild(idx);
  170. }
  171. return child;
  172. }
  173. deriveChild(index) {
  174. if (!this.pubKey || !this.chainCode) {
  175. throw new Error('No publicKey or chainCode set');
  176. }
  177. let data = toU32(index);
  178. if (index >= exports.HARDENED_OFFSET) {
  179. // Hardened
  180. const priv = this.privateKey;
  181. if (!priv) {
  182. throw new Error('Could not derive hardened child key');
  183. }
  184. // Hardened child: 0x00 || ser256(kpar) || ser32(index)
  185. data = (0, utils_1.concatBytes)(new Uint8Array([0]), priv, data);
  186. }
  187. else {
  188. // Normal child: serP(point(kpar)) || ser32(index)
  189. data = (0, utils_1.concatBytes)(this.pubKey, data);
  190. }
  191. const I = (0, hmac_1.hmac)(sha512_1.sha512, this.chainCode, data);
  192. const childTweak = bytesToNumber(I.slice(0, 32));
  193. const chainCode = I.slice(32);
  194. if (!secp256k1_1.secp256k1.utils.isValidPrivateKey(childTweak)) {
  195. throw new Error('Tweak bigger than curve order');
  196. }
  197. const opt = {
  198. versions: this.versions,
  199. chainCode,
  200. depth: this.depth + 1,
  201. parentFingerprint: this.fingerprint,
  202. index,
  203. };
  204. try {
  205. // Private parent key -> private child key
  206. if (this.privateKey) {
  207. const added = (0, modular_1.mod)(this.privKey + childTweak, secp256k1_1.secp256k1.CURVE.n);
  208. if (!secp256k1_1.secp256k1.utils.isValidPrivateKey(added)) {
  209. throw new Error('The tweak was out of range or the resulted private key is invalid');
  210. }
  211. opt.privateKey = added;
  212. }
  213. else {
  214. const added = Point.fromHex(this.pubKey).add(Point.fromPrivateKey(childTweak));
  215. // Cryptographically impossible: hmac-sha512 preimage would need to be found
  216. if (added.equals(Point.ZERO)) {
  217. throw new Error('The tweak was equal to negative P, which made the result key invalid');
  218. }
  219. opt.publicKey = added.toRawBytes(true);
  220. }
  221. return new HDKey(opt);
  222. }
  223. catch (err) {
  224. return this.deriveChild(index + 1);
  225. }
  226. }
  227. sign(hash) {
  228. if (!this.privateKey) {
  229. throw new Error('No privateKey set!');
  230. }
  231. (0, _assert_1.bytes)(hash, 32);
  232. return secp256k1_1.secp256k1.sign(hash, this.privKey).toCompactRawBytes();
  233. }
  234. verify(hash, signature) {
  235. (0, _assert_1.bytes)(hash, 32);
  236. (0, _assert_1.bytes)(signature, 64);
  237. if (!this.publicKey) {
  238. throw new Error('No publicKey set!');
  239. }
  240. let sig;
  241. try {
  242. sig = secp256k1_1.secp256k1.Signature.fromCompact(signature);
  243. }
  244. catch (error) {
  245. return false;
  246. }
  247. return secp256k1_1.secp256k1.verify(sig, hash, this.publicKey);
  248. }
  249. wipePrivateData() {
  250. this.privKey = undefined;
  251. if (this.privKeyBytes) {
  252. this.privKeyBytes.fill(0);
  253. this.privKeyBytes = undefined;
  254. }
  255. return this;
  256. }
  257. toJSON() {
  258. return {
  259. xpriv: this.privateExtendedKey,
  260. xpub: this.publicExtendedKey,
  261. };
  262. }
  263. serialize(version, key) {
  264. if (!this.chainCode) {
  265. throw new Error('No chainCode set');
  266. }
  267. (0, _assert_1.bytes)(key, 33);
  268. // version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33)
  269. return (0, utils_1.concatBytes)(toU32(version), new Uint8Array([this.depth]), toU32(this.parentFingerprint), toU32(this.index), this.chainCode, key);
  270. }
  271. }
  272. exports.HDKey = HDKey;
  273. //# sourceMappingURL=index.js.map