@stricahq/bip32ed25519
Version:
Pure javascript implementation of Bip32Ed25519, used for Cardano blockchain key pair.
114 lines (113 loc) • 4.45 kB
JavaScript
;
/* eslint-disable no-bitwise */
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const buffer_1 = require("buffer");
const bn_js_1 = __importDefault(require("bn.js"));
const pbkdf2_1 = require("pbkdf2");
const Bip32PublicKey_1 = __importDefault(require("./Bip32PublicKey"));
const PrivateKey_1 = __importDefault(require("./PrivateKey"));
const utils_1 = require("./utils");
const EDDSA = require("./ed25519e");
const eddsa = new EDDSA();
class Bip32PrivateKey {
constructor(xprv) {
this.xprv = xprv;
}
static fromEntropy(entropy) {
return new Promise((resolve, reject) => {
pbkdf2_1.pbkdf2("", entropy, 4096, 96, "sha512", (err, xprv) => {
if (err) {
reject(err);
}
// The lowest three bits of the first octet are cleared
// 248 or 0xf8 or 0b11111000
xprv[0] &= 0b11111000;
// the highest bit of the last octet is cleared
// 31 or 0x1f or 0b00011111
// AND the third highest bit is cleared too
xprv[31] &= 0b00011111;
// and the second highest bit of the last octet is set
// 64 or 0x40 or 0b01000000
xprv[31] |= 0b01000000;
resolve(new Bip32PrivateKey(xprv));
});
});
}
derive(index) {
const kl = this.xprv.slice(0, 32);
const kr = this.xprv.slice(32, 64);
const cc = this.xprv.slice(64, 96);
let z;
let i;
if (index < utils_1.HARDENED_OFFSET) {
const data = buffer_1.Buffer.allocUnsafe(1 + 32 + 4);
data.writeUInt32LE(index, 1 + 32);
const keyPair = eddsa.keyFromSecret(kl.toString("hex"));
const vk = buffer_1.Buffer.from(keyPair.pubBytes());
vk.copy(data, 1);
data[0] = 0x02;
z = utils_1.hmac512(cc, data);
data[0] = 0x03;
i = utils_1.hmac512(cc, data);
}
else {
const data = buffer_1.Buffer.allocUnsafe(1 + 64 + 4);
data.writeUInt32LE(index, 1 + 64);
kl.copy(data, 1);
kr.copy(data, 1 + 32);
data[0] = 0x00;
z = utils_1.hmac512(cc, data);
data[0] = 0x01;
i = utils_1.hmac512(cc, data);
}
const chainCode = i.slice(32, 64);
const zl = z.slice(0, 32);
const zr = z.slice(32, 64);
const left = new bn_js_1.default(kl, 16, "le")
.add(new bn_js_1.default(zl.slice(0, 28), 16, "le").mul(new bn_js_1.default(8)))
.toArrayLike(buffer_1.Buffer, "le", 32);
let right = new bn_js_1.default(kr, 16, "le")
.add(new bn_js_1.default(zr, 16, "le"))
.toArrayLike(buffer_1.Buffer, "le")
.slice(0, 32);
if (right.length !== 32) {
right = buffer_1.Buffer.from(right.toString("hex").padEnd(32, "0"), "hex");
}
const xprv = buffer_1.Buffer.concat([left, right, chainCode]);
return new Bip32PrivateKey(xprv);
}
deriveHardened(index) {
return this.derive(index + utils_1.HARDENED_OFFSET);
}
derivePath(path) {
const splitPath = path.split("/");
// @ts-ignore
return splitPath.reduce((hdkey, indexStr, i) => {
if (i === 0 && indexStr === "m") {
return hdkey;
}
if (indexStr.slice(-1) === `'`) {
const index = parseInt(indexStr.slice(0, -1), 10);
return hdkey.deriveHardened(index);
}
const index = parseInt(indexStr, 10);
return hdkey.derive(index);
}, this);
}
toBip32PublicKey() {
const keyPair = eddsa.keyFromSecret(this.xprv.slice(0, 32).toString("hex"));
const vk = buffer_1.Buffer.from(keyPair.pubBytes());
return new Bip32PublicKey_1.default(buffer_1.Buffer.concat([vk, this.xprv.slice(64, 96)]));
}
toBytes() {
return this.xprv;
}
toPrivateKey() {
const keyPair = eddsa.keyFromSecret(this.xprv.slice(0, 64));
return new PrivateKey_1.default(buffer_1.Buffer.from(keyPair.privBytes()));
}
}
exports.default = Bip32PrivateKey;