@safeheron/master-key-derive
Version:
Mnemonic Generation;
317 lines • 11.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extendedPubSharesAgg = exports.mnemonicToExtendedPub = exports.Mnemonics = exports.MasterKeyShare = exports.MasterKeyPair = exports.SigAlg = exports.Purpose = void 0;
const BN = require("bn.js");
const crypto_bip32_1 = require("@safeheron/crypto-bip32");
const crypto_utils_1 = require("@safeheron/crypto-utils");
const elliptic = require("elliptic");
const cryptoJS = require("crypto-js");
const bip39 = require("bip39");
const assert = require("assert");
const Ed25519 = new elliptic.eddsa('ed25519');
const Secp256k1 = new elliptic.ec('secp256k1');
const P256 = new elliptic.ec('p256');
const ZERO = new BN('0', 16);
const POW2_256 = new BN('1', 10).shln(256);
const MASTER_SECRET = cryptoJS.enc.Utf8.parse('MPC seed');
var Purpose;
(function (Purpose) {
Purpose[Purpose["MASTER_KEY_DERIVE"] = 0] = "MASTER_KEY_DERIVE";
Purpose[Purpose["AUTH_KEY_DERIVE"] = 1] = "AUTH_KEY_DERIVE";
Purpose[Purpose["POLICY_ENGINE"] = 2] = "POLICY_ENGINE";
})(Purpose = exports.Purpose || (exports.Purpose = {}));
var SigAlg;
(function (SigAlg) {
SigAlg[SigAlg["ECDSA_SECP256K1"] = 0] = "ECDSA_SECP256K1";
SigAlg[SigAlg["EDDSA_ED25519"] = 1] = "EDDSA_ED25519";
SigAlg[SigAlg["BLS_BN12_381"] = 2] = "BLS_BN12_381";
SigAlg[SigAlg["SCHNORR_SECP256K1"] = 3] = "SCHNORR_SECP256K1";
SigAlg[SigAlg["ECDSA_P256"] = 4] = "ECDSA_P256";
SigAlg[SigAlg["SCHNORR_P256"] = 5] = "SCHNORR_P256";
})(SigAlg = exports.SigAlg || (exports.SigAlg = {}));
function getOrderOfCurve(sigAlg) {
switch (sigAlg) {
case SigAlg.ECDSA_SECP256K1:
return Secp256k1.n;
case SigAlg.EDDSA_ED25519:
return Ed25519.curve.n;
case SigAlg.BLS_BN12_381:
throw "Unsupported sigAlg:" + sigAlg;
case SigAlg.SCHNORR_SECP256K1:
return Secp256k1.n;
case SigAlg.ECDSA_P256:
case SigAlg.SCHNORR_P256:
return P256.n;
default:
throw "Unsupported sigAlg:" + sigAlg;
}
}
function getHDKeyClass(sigAlg) {
switch (sigAlg) {
case SigAlg.ECDSA_SECP256K1:
return crypto_bip32_1.Secp256k1HDKey;
case SigAlg.EDDSA_ED25519:
return crypto_bip32_1.Ed25519HDKey;
case SigAlg.BLS_BN12_381:
throw "Can't get a HDKeyClass because of unsupported sigAlg: " + sigAlg;
case SigAlg.SCHNORR_SECP256K1:
return crypto_bip32_1.Secp256k1HDKey;
case SigAlg.ECDSA_P256:
case SigAlg.SCHNORR_P256:
return crypto_bip32_1.P256HDKey;
default:
throw "Can't get a HDKeyClass because of unsupported sigAlg: " + sigAlg;
}
}
function getPurposeHex(purpose) {
switch (purpose) {
case Purpose.MASTER_KEY_DERIVE:
return "00000000";
case Purpose.AUTH_KEY_DERIVE:
return "00000001";
case Purpose.POLICY_ENGINE:
return "00000002";
default:
throw "Unsupported purpose: " + purpose;
}
}
function getSigAlgHex(sigAlg) {
switch (sigAlg) {
case SigAlg.ECDSA_SECP256K1:
return "00000000";
case SigAlg.EDDSA_ED25519:
return "00000001";
case SigAlg.BLS_BN12_381:
return "00000002";
case SigAlg.SCHNORR_SECP256K1:
return "00000003";
case SigAlg.ECDSA_P256:
return "00000004";
case SigAlg.SCHNORR_P256:
return "00000005";
default:
throw "Unsupported sigAlg:" + sigAlg;
}
}
function getAlternativeHex(alter) {
return crypto_utils_1.Hex.pad8(alter.toString(16));
}
class MasterKeyPair {
constructor(xprv, xpub) {
this.xprv = xprv;
this.xpub = xpub;
}
static _checkIsValidChainCode(masterKeyShares) {
assert(masterKeyShares.length > 1);
// Check the state of 'isFullChainCode'
let flagFullChainCode = masterKeyShares[0].isFullChainCode;
for (let i = 1; i < masterKeyShares.length; i++) {
if (masterKeyShares[i].isFullChainCode !== flagFullChainCode) {
return false;
}
}
return true;
}
static _checkIsFullChainCode(masterKeyShares) {
assert(masterKeyShares.length > 1);
let c = masterKeyShares[0].chainCodeShare;
for (let i = 1; i < masterKeyShares.length; i++) {
if (!masterKeyShares[i].chainCodeShare.eq(c)) {
return false;
}
}
return true;
}
/**
* Recover master key pair from all the MasterKeyShares
* @param masterKeyShares
* @param sigAlg
*/
static recoverFromMasterKeyShares(masterKeyShares, sigAlg) {
assert(masterKeyShares.length > 1);
let n = getOrderOfCurve(sigAlg);
let c = new BN(0);
let k = new BN(0);
for (let mks of masterKeyShares) {
c = c.add(mks.chainCodeShare).umod(POW2_256);
k = k.add(mks.keyShare).umod(n);
}
if (k.eqn(0))
throw "Invalid master key!";
if (MasterKeyPair._checkIsFullChainCode(masterKeyShares)) {
c = masterKeyShares[0].chainCodeShare;
}
let HDKeyClass = getHDKeyClass(sigAlg);
let hdKey = HDKeyClass.fromPrivateKeyAndChainCode(k, c);
return new MasterKeyPair(hdKey.xprv, hdKey.xpub);
}
static _recoverFromMnemonics(mnemonics, sigAlg, alt) {
let masterKeyShares = [];
for (let i = 0; i < mnemonics.length; i++) {
masterKeyShares.push(mnemonics[i].derive(Purpose.MASTER_KEY_DERIVE, sigAlg, alt));
}
if (!MasterKeyPair._checkIsValidChainCode(masterKeyShares))
throw 'Invalid chain code!';
try {
return MasterKeyPair.recoverFromMasterKeyShares(masterKeyShares, sigAlg);
}
catch (err) {
return MasterKeyPair._recoverFromMnemonics(mnemonics, sigAlg, alt + 1);
}
}
/**
* Recover master key pair from all the Mnemonics
* @param mnemonics
* @param sigAlg
*/
static recoverFromMnemonics(mnemonics, sigAlg) {
return MasterKeyPair._recoverFromMnemonics(mnemonics, sigAlg, 0);
}
}
exports.MasterKeyPair = MasterKeyPair;
class MasterKeyShare {
constructor(keyShare, chainCodeShare, isFullChainCode) {
this.keyShare = keyShare;
this.chainCodeShare = chainCodeShare;
this.isFullChainCode = isFullChainCode;
}
}
exports.MasterKeyShare = MasterKeyShare;
class Mnemonics {
constructor(mnemo, extraMnemo) {
assert(mnemo.length > 0);
this.mnemo = mnemo;
this.extraMnemo = extraMnemo;
}
/***
* Create Mnemonics from key share and chain code.
*
* Warn: only invoked by the old user of mpc wallet
*
* @param keyShare
* @param chainCode
*/
static createMnemonics(keyShare, chainCode) {
assert(chainCode.lt(POW2_256));
assert(keyShare.lt(Secp256k1.n));
let mnemo = bip39.entropyToMnemonic(crypto_utils_1.Hex.pad64(keyShare.toString(16)));
let extraMnemo = bip39.entropyToMnemonic(crypto_utils_1.Hex.pad64(chainCode.toString(16)));
return new Mnemonics(mnemo, extraMnemo);
}
/**
* Create a new Mnemonic
*/
static generateMnemonic() {
let mnemo = bip39.generateMnemonic(256);
return new Mnemonics(mnemo, "");
}
_derive(purpose, sigAlg, alg) {
let seedHex = bip39.mnemonicToEntropy(this.mnemo);
let purposeHex = getPurposeHex(purpose);
let sigAlgHex = getSigAlgHex(sigAlg);
let alternativeHex = getAlternativeHex(alg);
// Derivation 0
let keyBytes = MASTER_SECRET;
let dataBytes = cryptoJS.enc.Hex.parse(seedHex);
let IHex = cryptoJS.enc.Hex.stringify(cryptoJS.HmacSHA512(dataBytes, keyBytes));
let IL_hex = IHex.substr(0, 64);
let IR_hex = IHex.substr(64);
// Derivation 1
keyBytes = cryptoJS.enc.Hex.parse(IL_hex);
dataBytes = cryptoJS.enc.Hex.parse(IR_hex + purposeHex);
IHex = cryptoJS.enc.Hex.stringify(cryptoJS.HmacSHA512(dataBytes, keyBytes));
IL_hex = IHex.substr(0, 64);
IR_hex = IHex.substr(64);
// Derivation 2
keyBytes = cryptoJS.enc.Hex.parse(IL_hex);
dataBytes = cryptoJS.enc.Hex.parse(IR_hex + sigAlgHex);
IHex = cryptoJS.enc.Hex.stringify(cryptoJS.HmacSHA512(dataBytes, keyBytes));
IL_hex = IHex.substr(0, 64);
IR_hex = IHex.substr(64);
// Derivation 3
keyBytes = cryptoJS.enc.Hex.parse(IL_hex);
dataBytes = cryptoJS.enc.Hex.parse(IR_hex + alternativeHex);
IHex = cryptoJS.enc.Hex.stringify(cryptoJS.HmacSHA512(dataBytes, keyBytes));
IL_hex = IHex.substr(0, 64);
IR_hex = IHex.substr(64);
let IL = new BN(0);
let IR = new BN(0);
if (sigAlg === SigAlg.EDDSA_ED25519) {
// IL: little-endian
IL = new BN(crypto_utils_1.Hex.reverseHex(IL_hex), 16);
// It doesn't matter for iR's encode.
IR = new BN(IR_hex, 16);
}
else {
IL = new BN(IL_hex, 16);
IR = new BN(IR_hex, 16);
}
let n = getOrderOfCurve(sigAlg);
let k = IL.umod(n);
let c = IR;
if (k.eqn(0)) {
return this._derive(purpose, sigAlg, alg + 1);
}
return new MasterKeyShare(k, c, false);
}
_derive_old_secp256k1() {
let n = getOrderOfCurve(SigAlg.ECDSA_SECP256K1);
let k = new BN(bip39.mnemonicToEntropy(this.mnemo), 16);
k = k.umod(n);
let c = new BN(bip39.mnemonicToEntropy(this.extraMnemo), 16);
return new MasterKeyShare(k, c, true);
}
/**
* Derive function
* @param purpose
* @param sigAlg
* @param alg
*/
derive(purpose, sigAlg, alg) {
if (this.extraMnemo === undefined || this.extraMnemo === "") {
return this._derive(purpose, sigAlg, alg);
}
else {
return this._derive_old_secp256k1();
}
}
}
exports.Mnemonics = Mnemonics;
function mnemonicToExtendedPub(sigAlg, alt, mnemo, chaincode = "") {
let HDKeyClass = getHDKeyClass(sigAlg);
let extraMnemo = "";
if (chaincode !== "") {
if (sigAlg !== SigAlg.ECDSA_SECP256K1)
throw 'Invalid sign algorithm!';
extraMnemo = bip39.entropyToMnemonic(crypto_utils_1.Hex.pad64(chaincode));
}
let mnemoObj = new Mnemonics(mnemo, extraMnemo);
let masterKeyShare = mnemoObj.derive(Purpose.MASTER_KEY_DERIVE, sigAlg, alt);
let hdKey = HDKeyClass.fromPrivateKeyAndChainCode(masterKeyShare.keyShare, masterKeyShare.chainCodeShare);
return hdKey.xpub;
}
exports.mnemonicToExtendedPub = mnemonicToExtendedPub;
function extendedPubSharesAgg(sigAlg, extendedPubShares, isFullChainCode) {
assert(extendedPubShares.length > 1);
let HDKeyClass = getHDKeyClass(sigAlg);
let hdKey = HDKeyClass.fromExtendedKey(extendedPubShares[0]);
let chaincode = hdKey.chainCode;
let pub = hdKey.publicKey;
for (let i = 1; i < extendedPubShares.length; i++) {
hdKey = HDKeyClass.fromExtendedKey(extendedPubShares[i]);
pub = pub.add(hdKey.publicKey);
if (isFullChainCode) {
if (!chaincode.eq(hdKey.chainCode))
throw "Invalid chaincode!";
}
else {
chaincode = chaincode.add(hdKey.chainCode).umod(POW2_256);
}
}
if (pub.isInfinity())
throw "Invalid master public key!";
hdKey = HDKeyClass.fromPublicKeyAndChainCode(pub, chaincode);
return hdKey.xpub;
}
exports.extendedPubSharesAgg = extendedPubSharesAgg;
//# sourceMappingURL=keyDerive.js.map