Added new op, KeyToExtendedKey. This takes a private key, with chaincode as parameter, and makes a master key. Public and Private master keys are allowable.

This commit is contained in:
David C Goldenberg 2025-06-13 11:10:08 -04:00
parent a060ec446e
commit 94957cfbbb
2 changed files with 169 additions and 0 deletions

View file

@ -0,0 +1,87 @@
/**
* @author dcgoldenberg [dgoldenberg@mitre.org]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { makeSureIsHex, serializeExtendedKeyFunc, getExtendedKeyVersion } from "../lib/Bitcoin.mjs";
/**
* Key To Extended Key operation
*/
class KeyToExtendedKey extends Operation {
/**
* KeyToExtendedKey constructor
*/
constructor() {
super();
this.name = "Key To Extended Key";
this.module = "Default";
this.description = "Turns a key, with chaincode and version as parameters, into an extended key. We assume the key is meant to be a master key, so depth and child number are set to 0, and fingerprint is set to 00000000.";
this.infoURL = "https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki"; // Usually a Wikipedia link. Remember to remove localisation (i.e. https://wikipedia.org/etc rather than https://en.wikipedia.org/etc)
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Chaincode",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex"]
},
{
"name": "Version Type",
"type": "option",
"value": ["xpub", "xprv", "ypub", "yprv", "zpub", "zprv", "Zpub", "Zprv", "Ypub", "Yprv", "Ltub", "Ltpv", "Mtub", "Mtpv", "ttub", "ttpv", "tpub", "tprv", "upub", "uprv", "vpub", "vprv", "Upub", "Uprv", "Vpub", "Vprv"]
}
/* Example arguments. See the project wiki for full details.
{
name: "First arg",
type: "string",
value: "Don't Panic"
},
{
name: "Second arg",
type: "number",
value: 42
}
*/
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
// const [firstArg, secondArg] = args;
const inputAsHex = makeSureIsHex(input);
const isPublic = inputAsHex.length === 66 && (inputAsHex.startsWith("03") || inputAsHex.startsWith("02"));
const isPrivate = inputAsHex.length === 64;
const privateVersions = ["xprv", "yprv", "zprv", "Zprv", "Yprv", "Ltpv", "Mtpv", "ttpv", "tprv", "uprv", "vprv", "Uprv", "Vprv"];
if (!isPublic && !isPrivate) {
throw new OperationError("Error: String " + inputAsHex + " is not a valid public or private key.");
}
if (isPublic && privateVersions.indexOf(args[1]) !== -1) {
throw new OperationError("Error: Mis-Match between version and key type. Public Key is entered, but a private version is selected.");
}
if (isPrivate && privateVersions.indexOf(args[1]) === -1) {
throw new OperationError("Error: Mis-Match between version and key type. Private Key is entered, but a public version is selected.");
}
const key = isPrivate ? "00" + inputAsHex : inputAsHex;
const newVersion = getExtendedKeyVersion(args[1]);
const newExtendedKey = serializeExtendedKeyFunc(newVersion, 0, "00000000", 0, args[0].string, key);
return newExtendedKey;
}
}
export default KeyToExtendedKey;

View file

@ -0,0 +1,82 @@
/**
* Key To Extended Key Tests.
*
* @author dgoldenberg [virtualcurrency@mitre.org]
* @copyright MITRE 2023
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Key To Extended Key (Basic, XPRV)",
input: "f55acd736ff0f80f2cdc56ab5ac1e4d4ce92e1f46d137a282a61e706681df9c5",
expectedOutput: "xprv9s21ZrQH143K4bXtdSLbsWGSfAok775A1bF3YwPeHe8ePa7QwD5V4kK1RmdZV2M6TKVfszKt19UaHfyqdQ2MZ8dv2t7G2Tvtxnef7Pxu8Qu",
recipeConfig: [
{
"op": "Key To Extended Key",
"args": [
{
"option": "Hex",
"string": "fedf6c5ebcc2fc7b66291e55501a005886128bf97aeced3a91478a0c44f54dbe"
},
"xprv"
]
}
],
},
{
name: "Key To Extended Key (YPRV Same Data)",
input: "f55acd736ff0f80f2cdc56ab5ac1e4d4ce92e1f46d137a282a61e706681df9c5",
expectedOutput: "yprvABrGsX5C9janutj1To8E5bMwq8xC3j4evhmGLLHXfeWXSfveBsF3goy9Syb9Uw11rxcUdTvSToq8AxbQM6SNMNKWuDogcNkPEWiJVuszXFR",
recipeConfig: [
{
"op": "Key To Extended Key",
"args": [
{
"option": "Hex",
"string": "fedf6c5ebcc2fc7b66291e55501a005886128bf97aeced3a91478a0c44f54dbe"
},
"yprv"
]
}
],
},
{
name: "Key To Extended Key (Public Key With Private Version), Should Throw Error.",
input: "02233f618dad285dc8b03eb7bf9a59dec039b7c1b2433dabdca636e17e10890e85",
expectedOutput: "Error: Mis-Match between version and key type. Public Key is entered, but a private version is selected.",
recipeConfig: [
{
"op": "Key To Extended Key",
"args": [
{
"option": "Hex",
"string": "cda6685894f356bdf1bd6af6cb7e3961e550fb9c9b2c82d9ebfd89f48c2afea6"
},
"xprv"
]
}
],
},
{
name: "Key To Extended Key (Private Key With Public Version), Should Throw Error.",
input: "f55acd736ff0f80f2cdc56ab5ac1e4d4ce92e1f46d137a282a61e706681df9c5",
expectedOutput: "Error: Mis-Match between version and key type. Private Key is entered, but a public version is selected.",
recipeConfig: [
{
"op": "Key To Extended Key",
"args": [
{
"option": "Hex",
"string": "fedf6c5ebcc2fc7b66291e55501a005886128bf97aeced3a91478a0c44f54dbe"
},
"xpub"
]
}
],
},
]);