Hotfix: Fixed a bug where if a private or public key ended with a0 or other whitespace byte, the validatePrivate or Public Key function in Bitcoin.mjs would strip that byte. In addition added a Public Key To TRX Address Operation.

This commit is contained in:
David C Goldenberg 2024-10-06 21:25:48 -04:00
parent e96e31cd52
commit f7ebae4e88
5 changed files with 202 additions and 24 deletions

View file

@ -355,7 +355,8 @@
"Seed To Master Key", "Seed To Master Key",
"Decrypt Keystore File", "Decrypt Keystore File",
"BIP32Derive", "BIP32Derive",
"Public Key To ETH Style Address" "Public Key To ETH Style Address",
"Public Key To TRX Style Address"
] ]
}, },
{ {

View file

@ -28,7 +28,7 @@ function validateLengths(input, allowableLengths) {
*/ */
function isHex(input) { function isHex(input) {
const re = /^[0-9A-Fa-f]{2,}$/g; const re = /^[0-9A-Fa-f]{2,}$/g;
return re.test(input); return re.test(input) && input.length %2 === 0;
} }
/** /**
@ -49,14 +49,13 @@ function isValidBytes(input) {
* @param {*} input * @param {*} input
*/ */
export function validatePrivateKey(input) { export function validatePrivateKey(input) {
const curInput = input.trim(); if (!validateLengths(input, [32, 64])) {
if (!validateLengths(curInput, [32, 64])) { return "Invalid length. We want either 32 or 64 but we got: " + input.length;
return "Invalid length. We want either 32 or 64 but we got: " + curInput.length;
} }
if (curInput.length === 64 && !isHex(curInput)) { if (input.length === 64 && !isHex(input)) {
return "We have a string of length 64, but not valid hex. Cannot be interpreted as a private key."; return "We have a string of length 64, but not valid hex. Cannot be interpreted as a private key.";
} }
if (curInput.length === 32 && !isValidBytes(curInput)) { if (input.length === 32 && !isValidBytes(input)) {
return "We have a string of length 32 but cannot cannot be interpreted as valid bytes."; return "We have a string of length 32 but cannot cannot be interpreted as valid bytes.";
} }
return ""; return "";
@ -70,31 +69,30 @@ export function validatePrivateKey(input) {
* @param {*} input * @param {*} input
*/ */
export function validatePublicKey(input) { export function validatePublicKey(input) {
const curInput = input.trim(); if (!validateLengths(input, [33, 65, 66, 130])) {
if (!validateLengths(curInput, [33, 65, 66, 130])) { return "Invalid length. We want either 33, 65 (if bytes) or 66, 130 (if hex) but we got: " + input.length;
return "Invalid length. We want either 33, 65 (if bytes) or 66, 130 (if hex) but we got: " + curInput.length;
} }
if (isHex(curInput)) { if (isHex(input)) {
if (!validateLengths(curInput, [66, 130])) { if (!validateLengths(input, [66, 130])) {
return "We have a hex string, but its length is wrong. We want 66, 130 but we got: " + curInput.length; return "We have a hex string, but its length is wrong. We want 66, 130 but we got: " + input.length;
} }
if (curInput.length === 66 && (curInput.slice(0, 2) !== "02" && curInput.slice(0, 2) !== "03")) { if (input.length === 66 && (input.slice(0, 2) !== "02" && input.slice(0, 2) !== "03")) {
return "We have a valid hex string, of reasonable length, (66) but doesn't start with the right value. Correct values are 02, or 03 but we have: " + curInput.slice(0, 2); return "We have a valid hex string, of reasonable length, (66) but doesn't start with the right value. Correct values are 02, or 03 but we have: " + input.slice(0, 2);
} }
if (curInput.length === 130 && curInput.slice(0, 2) !== "04") { if (input.length === 130 && input.slice(0, 2) !== "04") {
return "We have a valid hex string of reasonable length, (130) but doesn't start with the right value. Correct values are 04 but we have: " + curInput.slice(0, 2); return "We have a valid hex string of reasonable length, (130) but doesn't start with the right value. Correct values are 04 but we have: " + input.slice(0, 2);
} }
return ""; return "";
} }
if (isValidBytes(curInput)) { if (isValidBytes(input)) {
if (!validateLengths(curInput, [33, 65])) { if (!validateLengths(input, [33, 65])) {
return "We have a byte string, but its length is wrong. We want 33 or 65 but we got: " + curInput.length; return "We have a byte string, but its length is wrong. We want 33 or 65 but we got: " + input.length;
} }
if (curInput.length === 33 && toHex(curInput[0]) !== "02" && toHex(curInput[0]) !== "03") { if (input.length === 33 && toHex(input[0]) !== "02" && toHex(input[0]) !== "03") {
return "We have a valid byte string, of reasonable length, (33) but doesn't start with the right value. Correct values are 02, or 03 but we have: " + toHex(curInput[0]) ; return "We have a valid byte string, of reasonable length, (33) but doesn't start with the right value. Correct values are 02, or 03 but we have: " + toHex(input[0]) ;
} }
if (curInput.length === 65 && toHex(curInput[0]) !== "04") { if (input.length === 65 && toHex(input[0]) !== "04") {
return "We have a valid byte string, of reasonable length, (65) but doesn't start with the right value. Correct value is 04 but we have: " + toHex(curInput[0]); return "We have a valid byte string, of reasonable length, (65) but doesn't start with the right value. Correct value is 04 but we have: " + toHex(input[0]);
} }
return ""; return "";
} }

View file

@ -0,0 +1,81 @@
/**
* @author dgoldenberg [virtualcurrency@mitre.org]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import {makeSureIsBytes, validatePublicKey, base58Encode, doubleSHA} from "../lib/Bitcoin.mjs";
import { fromArrayBuffer } from "crypto-api/src/encoder/array-buffer.mjs";
import {toHex} from "crypto-api/src/encoder/hex.mjs";
import JSSHA3 from "js-sha3";
import Utils from "../Utils.mjs";
import ec from "elliptic";
/**
* Turns a public key into an ETH address.
* @param {*} input Input, a public key in hex or bytes.
*/
function pubKeyToTRXAddress(input) {
// Ethereum addresses require uncompressed public keys.
// We convert if the public key is compressed.
let curKey = makeSureIsBytes(input);
if (curKey[0] !== 0x04 || curKey.length !== 65) {
const ecContext = ec.ec("secp256k1");
const thisKey = ecContext.keyFromPublic(curKey);
curKey = thisKey.getPublic(false, "hex");
}
const algo = JSSHA3.keccak256;
// We need to redo the hex-> bytes transformation here because Javascript is silly.
// sometimes what is desired is an array of ints.
// Other times a string
// Here, the Keccak algorithm seems to want an array of ints. (sigh)
const result = algo(Utils.convertToByteArray(curKey, "hex").slice(1,));
const unencodedAddress = result.slice(-40);
const checksumHash = toHex(doubleSHA(fromArrayBuffer(Utils.convertToByteArray("41" + unencodedAddress, "hex"))));
const finalString = "41" + unencodedAddress + checksumHash.slice(0, 8);
const address = base58Encode(Utils.convertToByteArray(finalString, "hex"));
return address;
}
/**
* Public Key To TRX Style Address operation
*/
class PublicKeyToTRXStyleAddress extends Operation {
/**
* PublicKeyToTRXStyleAddress constructor
*/
constructor() {
super();
this.name = "Public Key To TRX Style Address";
this.module = "Default";
this.description = "Converts a public key, (33 bytes beginning with 02 or 03 for compressed or 65 bytes beginning with 04) to a TRX style address. This involves hashing the public key using keccack-256 like Ethereum, but encoding the result using base58 encoding. ";
this.infoURL = "https://developers.tron.network/docs/account";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
// We check if input is blank.
// If its blank or just whitespace, we don't need to bother dealing with it.
if (input.trim().length === 0) {
return "";
}
if (validatePublicKey(input) !== "") {
return validatePublicKey(input);
}
return pubKeyToTRXAddress(input);
}
}
export default PublicKeyToTRXStyleAddress;

View file

@ -183,6 +183,7 @@ import "./tests/WIFToPrivateKey.mjs";
import "./tests/SeedphraseToSeed.mjs"; import "./tests/SeedphraseToSeed.mjs";
import "./tests/DeserializeExtendedKey.mjs"; import "./tests/DeserializeExtendedKey.mjs";
import "./tests/PublicKeyToETHStyleAddress.mjs"; import "./tests/PublicKeyToETHStyleAddress.mjs";
import "./tests/PublicKeyToTRXStyleAddress.mjs";
import "./tests/GetAllCasings.mjs"; import "./tests/GetAllCasings.mjs";
import "./tests/SIGABA.mjs"; import "./tests/SIGABA.mjs";
import "./tests/ELFInfo.mjs"; import "./tests/ELFInfo.mjs";

View file

@ -0,0 +1,97 @@
/**
* Public Key to TRX Style Address Cryptocurrency Address tests.
*
* @author dgoldenberg [virtualcurrency@mitre.org]
* @copyright MITRE 2023
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Public Key To TRX Style Address",
input: "04187ac6bc2723630c936e363b826de17dac62382e3bbfabf306ad5f55cc79538783889fe32946b52092dad24c56893d522413d67e62b28f6c54f14821367a9edc",
expectedOutput: "THV2shRZn4cam7aQreAg9aixfk2sTcho6r",
recipeConfig: [
{
"op": "Public Key To TRX Style Address",
"args": []
},
],
},
{
name: "Public Key To TRX Style Address Compressed Key",
input: "02d1b5855d3f99c4449eb7af576bec1b9bc0bf0769446820686d2de5c47c13b1a0",
expectedOutput: "TQS4NjvDN4TxcZd8LwD1eAKv9y4vSVkDW2",
recipeConfig: [
{
"op": "Public Key To TRX Style Address",
"args": []
},
],
},
{
name: "Public Key to ETH Style Address: Compressed Key 2",
input: "03a85e8f6fc71898b5c3347decd2c0bba8abb99393c8358fcf0bca72e4c7d68514",
expectedOutput: "TXBP2ebjZsnDEL9X5xCZSZZ6FC3Vppccv4",
recipeConfig: [
{
"op": "Public Key To TRX Style Address",
"args": []
},
],
},
{
name: "Public Key to ETH Style Address: Compressed Key 2 (From Hex)",
input: "03a85e8f6fc71898b5c3347decd2c0bba8abb99393c8358fcf0bca72e4c7d68514",
expectedOutput: "TXBP2ebjZsnDEL9X5xCZSZZ6FC3Vppccv4",
recipeConfig: [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "Public Key To TRX Style Address",
"args": [],
},
],
},
{
name: "Public Key to TRX Style Address: Compressed Key (From Hex)",
input: "02d1b5855d3f99c4449eb7af576bec1b9bc0bf0769446820686d2de5c47c13b1a0",
expectedOutput: "TQS4NjvDN4TxcZd8LwD1eAKv9y4vSVkDW2",
recipeConfig: [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "Public Key To TRX Style Address",
"args": []
},
],
},
{
name: "Public Key To TRX Style Address (From Hex)",
input: "04187ac6bc2723630c936e363b826de17dac62382e3bbfabf306ad5f55cc79538783889fe32946b52092dad24c56893d522413d67e62b28f6c54f14821367a9edc",
expectedOutput: "THV2shRZn4cam7aQreAg9aixfk2sTcho6r",
recipeConfig: [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "Public Key To TRX Style Address",
"args": []
},
],
},
]);