182 lines
5.9 KiB
JavaScript
182 lines
5.9 KiB
JavaScript
(function (EXPORTS) {
|
|
"use strict";
|
|
const hederaCrypto = EXPORTS;
|
|
|
|
function hexToBytes(hex) {
|
|
const bytes = [];
|
|
for (let i = 0; i < hex.length; i += 2) {
|
|
bytes.push(parseInt(hex.substr(i, 2), 16));
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
function bytesToHex(bytes) {
|
|
return Array.from(bytes)
|
|
.map(b => b.toString(16).padStart(2, '0'))
|
|
.join('');
|
|
}
|
|
|
|
// Generate a new random key
|
|
function generateNewID() {
|
|
var key = new Bitcoin.ECKey(false);
|
|
key.setCompressed(true);
|
|
return {
|
|
floID: key.getBitcoinAddress(),
|
|
pubKey: key.getPubKeyHex(),
|
|
privKey: key.getBitcoinWalletImportFormat(),
|
|
};
|
|
}
|
|
|
|
Object.defineProperties(hederaCrypto, {
|
|
newID: {
|
|
get: () => generateNewID(),
|
|
},
|
|
hashID: {
|
|
value: (str) => {
|
|
let bytes = ripemd160(Crypto.SHA256(str, { asBytes: true }), {
|
|
asBytes: true,
|
|
});
|
|
bytes.unshift(bitjs.pub);
|
|
var hash = Crypto.SHA256(Crypto.SHA256(bytes, { asBytes: true }), {
|
|
asBytes: true,
|
|
});
|
|
var checksum = hash.slice(0, 4);
|
|
return bitjs.Base58.encode(bytes.concat(checksum));
|
|
},
|
|
},
|
|
tmpID: {
|
|
get: () => {
|
|
let bytes = Crypto.util.randomBytes(20);
|
|
bytes.unshift(bitjs.pub);
|
|
var hash = Crypto.SHA256(Crypto.SHA256(bytes, { asBytes: true }), {
|
|
asBytes: true,
|
|
});
|
|
var checksum = hash.slice(0, 4);
|
|
return bitjs.Base58.encode(bytes.concat(checksum));
|
|
},
|
|
},
|
|
});
|
|
|
|
// --- Multi-chain Generator (BTC, FLO, HBAR) ---
|
|
hederaCrypto.generateMultiChain = async function (inputWif) {
|
|
const versions = {
|
|
BTC: { pub: 0x00, priv: 0x80 },
|
|
FLO: { pub: 0x23, priv: 0xa3 },
|
|
};
|
|
|
|
const origBitjsPub = bitjs.pub;
|
|
const origBitjsPriv = bitjs.priv;
|
|
const origBitjsCompressed = bitjs.compressed;
|
|
const origCoinJsCompressed = coinjs.compressed;
|
|
|
|
bitjs.compressed = true;
|
|
coinjs.compressed = true;
|
|
|
|
let privKeyHex;
|
|
let compressed = true;
|
|
|
|
// --- Decode input or generate new ---
|
|
if (typeof inputWif === "string" && inputWif.trim().length > 0) {
|
|
const trimmedInput = inputWif.trim();
|
|
const hexOnly = /^[0-9a-fA-F]+$/.test(trimmedInput);
|
|
|
|
if (hexOnly && (trimmedInput.length === 64 || trimmedInput.length === 128)) {
|
|
privKeyHex =
|
|
trimmedInput.length === 128 ? trimmedInput.substring(0, 64) : trimmedInput;
|
|
} else {
|
|
try {
|
|
const decode = Bitcoin.Base58.decode(trimmedInput);
|
|
const keyWithVersion = decode.slice(0, decode.length - 4);
|
|
let key = keyWithVersion.slice(1);
|
|
if (key.length >= 33 && key[key.length - 1] === 0x01) {
|
|
key = key.slice(0, key.length - 1);
|
|
compressed = true;
|
|
}
|
|
privKeyHex = bytesToHex(key);
|
|
} catch (e) {
|
|
console.warn("Invalid WIF, generating new key:", e);
|
|
const newKey = generateNewID();
|
|
const decode = Bitcoin.Base58.decode(newKey.privKey);
|
|
const keyWithVersion = decode.slice(0, decode.length - 4);
|
|
let key = keyWithVersion.slice(1);
|
|
if (key.length >= 33 && key[key.length - 1] === 0x01)
|
|
key = key.slice(0, key.length - 1);
|
|
privKeyHex = bytesToHex(key);
|
|
}
|
|
}
|
|
} else {
|
|
// Generate new key if no input
|
|
const newKey = generateNewID();
|
|
const decode = Bitcoin.Base58.decode(newKey.privKey);
|
|
const keyWithVersion = decode.slice(0, decode.length - 4);
|
|
let key = keyWithVersion.slice(1);
|
|
if (key.length >= 33 && key[key.length - 1] === 0x01)
|
|
key = key.slice(0, key.length - 1);
|
|
privKeyHex = bytesToHex(key);
|
|
}
|
|
|
|
// --- Derive addresses for each chain ---
|
|
const result = { BTC: {}, FLO: {}, HBAR: {} };
|
|
|
|
// BTC
|
|
bitjs.pub = versions.BTC.pub;
|
|
bitjs.priv = versions.BTC.priv;
|
|
const pubKeyBTC = bitjs.newPubkey(privKeyHex);
|
|
result.BTC.address = coinjs.bech32Address(pubKeyBTC).address;
|
|
result.BTC.privateKey = bitjs.privkey2wif(privKeyHex);
|
|
|
|
// FLO
|
|
bitjs.pub = versions.FLO.pub;
|
|
bitjs.priv = versions.FLO.priv;
|
|
const pubKeyFLO = bitjs.newPubkey(privKeyHex);
|
|
result.FLO.address = bitjs.pubkey2address(pubKeyFLO);
|
|
result.FLO.privateKey = bitjs.privkey2wif(privKeyHex);
|
|
|
|
// HBAR (Hedera)
|
|
try {
|
|
const privBytes = hexToBytes(privKeyHex.substring(0, 64));
|
|
|
|
// Create ECDSA key from private key bytes
|
|
const ecKey = new Bitcoin.ECKey(privBytes);
|
|
ecKey.setCompressed(false); // Uncompressed for EVM address derivation
|
|
|
|
// Get uncompressed public key (65 bytes: 04 + 32 bytes X + 32 bytes Y)
|
|
const pubKeyHex = ecKey.getPubKeyHex();
|
|
|
|
|
|
// Derive EVM address from public key using Keccak-256
|
|
// Remove '04' prefix and hash the remaining 64 bytes
|
|
const pubKeyBytes = pubKeyHex.substring(2);
|
|
|
|
// Use web3.js for proper Keccak-256 hash (Ethereum standard)
|
|
const hash = Web3.utils.keccak256('0x' + pubKeyBytes);
|
|
// hash is '0x...' format, take last 20 bytes (40 hex chars)
|
|
const evmAddress = '0x' + hash.substring(26);
|
|
|
|
// Compressed public key for display
|
|
ecKey.setCompressed(true);
|
|
const compressedPubKey = ecKey.getPubKeyHex();
|
|
|
|
result.HBAR.evmAddress = evmAddress;
|
|
result.HBAR.publicKey = compressedPubKey;
|
|
result.HBAR.privateKey = privKeyHex.substring(0, 64);
|
|
result.HBAR.address = evmAddress; // EVM address
|
|
} catch (error) {
|
|
console.error("Error generating HBAR keys:", error);
|
|
result.HBAR.evmAddress = "Error generating address";
|
|
result.HBAR.publicKey = "Error";
|
|
result.HBAR.privateKey = privKeyHex;
|
|
result.HBAR.address = "Error";
|
|
}
|
|
|
|
bitjs.pub = origBitjsPub;
|
|
bitjs.priv = origBitjsPriv;
|
|
bitjs.compressed = origBitjsCompressed;
|
|
coinjs.compressed = origCoinJsCompressed;
|
|
|
|
return result;
|
|
};
|
|
|
|
|
|
})(typeof module === "object" ? module.exports : (window.hederaCrypto = {}));
|