// import bitcoin from 'bitcoinjs-lib'
// import TESTNET from 'bitcoin'
// import nostrTools from 'nostr-tools'
// const { getPublicKey } = nostrTools
import { ECPairFactory } from 'ecpair'
import * as secp256k1 from '@noble/secp256k1'
// import { bech32 } from '@scure/base'
import ed25519 from 'ed25519'
import { bech32 } from 'bech32'
// import * as tiny from 'tiny-secp256k1'
/**
* getPublicKey
*
* Gets a public key from a given private key
*
* @param {Buffer|Uint8Array} privateKey - The private key to use to generate the public key
* @returns {String} Hex encoded public key
*/
function getPublicKey(privateKey) {
return secp256k1.utils.bytesToHex(secp256k1.schnorr.getPublicKey(privateKey))
}
/**
* Function to convert a hexadecimal data to a bech32 encoded string
* @param {string} data - Hexadecimal data to be encoded
* @param {string} prefix - Prefix of the bech32 encoded string
* @returns {string} bech32 encoded string
*/
function nip19(data, prefix) {
// console.log(data)
const words = bech32.toWords(Buffer.from(data, 'hex'))
return bech32.encode(prefix, words)
}
const TESTNET = {
messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'tb',
bip32: {
public: 0x043587cf,
private: 0x04358394
},
pubKeyHash: 0x6f,
scriptHash: 0xc4,
wif: 0xef
}
// FUNCTIONS
/**
* Encode bytes into bech32 format
* @param {string} prefix
* @param {string} hex
* @returns {string} bech32 encoded string
*/
function encodeBytes(prefix, hex) {
const data = secp256k1.utils.hexToBytes(hex)
const words = bech32.toWords(data)
return bech32.encode(prefix, words, 1500)
}
/**
* Decode bech32 encoded bytes
* @param {string} bech32EncodedString
* @returns {string} hex
* @throws {Error} if input is not a valid bech32 encoded string
*/
function decodeBytes(bech32EncodedString) {
const { words } = bech32.decode(bech32EncodedString)
const data = bech32.fromWords(words)
const hex = data.map(byte => byte.toString(16).padStart(2, '0')).join('')
return hex
}
// Convert a hex string to a buffer
// This function is used to convert a hex string to a buffer
// which is required by the bitcoinjs-lib library
// hex - the hex string to convert
// returns the buffer
const hexToBuffer = hex => Buffer.from(hex, 'hex')
/**
* Generates a public key from a given private key
*
* @param {string} privKey - The private key used to generate the public key
*
* @returns {string} The generated public key in hexadecimal format
*/
function generate_public_key(privKey) {
return ed25519.MakeKeypair(hexToBuffer(privKey)).publicKey.toString('hex')
}
/**
* Converts a given buffer to a hexadecimal string.
* @param {Buffer} buffer - The buffer to convert.
* @returns {string} The hexadecimal string.
*/
function bufferToHex(buffer) {
return Array.prototype.map
.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2))
.join('')
}
/**
* Converts a hexadecimal string to a base64 encoded string
* @param {string} str A string of hexadecimal characters
* @returns {string} A base64 encoded string
*/
function hexToBase64(str) {
return Buffer.from(str, 'hex').toString('base64')
}
/**
* This function adds two secp256k1 keys together using the XOR operator.
*
* @param {string} keyA - The first secp256k1 key to be added.
* @param {string} keyB - The second secp256k1 key to be added.
*
* @returns {string} The result of the addition of the two keys in hexadecimal format.
*/
function secp256k1Add(keyA, keyB) {
// xor is a binary operator that returns a 1 in each bit position for which the corresponding bits of its operands are different
const xorKeyA = BigInt('0x' + keyA.slice(0, 64))
const xorKeyB = BigInt('0x' + keyB.slice(0, 64))
// xor the two keys
const xorAdd = xorKeyA + xorKeyB
// convert the result to hex
const hexAdd = xorAdd.toString(16).padStart(64, '0')
const result = `${hexAdd.slice(64)}`
return result
}
class Base64 {
static encode(bin) {
const ss = []
bin.map(b => ss.push(String.fromCharCode(b)))
return btoa(ss.join(''))
}
static decode(text) {
const s = atob(text)
const res = new Uint8Array(s.length)
for (let i = 0; i < s.length; i++) {
res[i] = s.charCodeAt(i)
}
return res
}
}
const bin2s = (bin, n, len) => {
const b = new Uint8Array(len)
for (let i = 0; i < len; i++) {
b[i] = bin[i + n]
}
return new TextDecoder().decode(b)
}
const bin2i = (bin, n) => {
return ((bin[n] & 0xff) << 24) | ((bin[n + 1] & 0xff) << 16) | ((bin[n + 2] & 0xff) << 8) | (bin[n + 3] & 0xff)
}
const subbin = (bin, n, len) => {
const b = new Uint8Array(len)
for (let i = 0; i < len; i++) {
b[i] = bin[i + n]
}
return b
}
const setbin = (bin, off, b) => {
for (let i = 0; i < b.length; i++) {
bin[i + off] = b[i]
}
return bin
}
const i2bin = (b, off, n) => {
b[off] = (n >> 24) & 0xff
b[off + 1] = (n >> 16) & 0xff
b[off + 2] = (n >> 8) & 0xff
b[off + 3] = n & 0xff
}
const s2bin = (bin, off, s) => {
setbin(bin, off, new TextEncoder().encode(s))
}
const bincat = (bin1, bin2) => {
const bin = new Uint8Array(bin1.length + bin2.length)
for (let i = 0; i < bin1.length; i++) {
bin[i] = bin1[i]
}
for (let i = 0; i < bin2.length; i++) {
bin[i + bin1.length] = bin2[i]
}
return bin
}
const eqbin = (bin1, bin2) => {
if (bin1 == bin2) {
return true
}
if (!bin1 || !bin2) {
return false
}
if (bin1.length != bin2.length) {
return false
}
for (let i = 0; i < bin1.length; i++) {
if (bin1[i] != bin2[i]) {
return false
}
}
return true
}
const BEGIN = '-----BEGIN OPENSSH PRIVATE KEY-----'
const END = '-----END OPENSSH PRIVATE KEY-----'
/**
* This function decodes a PEM (Privacy Enhanced Mail) string and returns an object containing the public and private key.
*
* @param {string} txt - The PEM string to decode.
* @returns {Object} An object containing the public and private key, or null if the string could not be decoded.
*/
function decodePEM(txt) {
const ss = txt.split('\n').map(s => s.trim())
const ns = ss.indexOf(BEGIN)
const ne = ss.indexOf(END)
if (ns < 0 || ne < 0 || ns > ne) {
return null
}
const bin = Base64.decode(ss.slice(ns + 1, ne).join(''))
if (bin2s(bin, 0x2f, 11) != 'ssh-ed25519') {
return null
}
if (bin2s(bin, 0x6e, 11) != 'ssh-ed25519') {
return null
}
if (bin2i(bin, 0x3a) != 32) {
return null
}
const publicKey = subbin(bin, 0x3e, 32)
if (bin2i(bin, 0x9d) != 64) {
return null
}
const privateKey = subbin(bin, 0xa1, 64)
return { publicKey, privateKey }
}
/**
* Splits a long string into multiple lines of a specified character limit
* @param {string} str - The string to be split
* @returns {string} The splitted string
*/
function splitLongStrings(str) {
const charLimit = 70
let splittedString = ''
if (str.length > charLimit) {
for (let i = 0; i < str.length; i++) {
if (i % charLimit === 0) {
splittedString += '\n'
}
splittedString += str[i]
}
return splittedString
} else {
return str
}
}
/**
* This function encodes a given set of keys in PEM format.
*
* @param {Object} keys An object containing the public and private keys.
* @returns {String} A string representing the encoded PEM format.
*/
function encodePEM(keys) {
const pem = `6f70656e7373682d6b65792d763100000000046e6f6e65000000046e6f6e650000000000000001000000330000000b7373682d6564323535313900000020${keys.publicKey}000000980ac771850ac771850000000b7373682d6564323535313900000020${keys.publicKey}00000040${keys.privateKey}${keys.publicKey}000000126d656c76696e406d656c76696e2d504e3632010203`
return `${BEGIN}${splitLongStrings(hexToBase64(pem))}
${END}`
}
/**
* Generates various keys and related information from a given private key using different algorithms.
*
* @param {string} privateKey - The private key to generate keys from.
* @returns {Object} An object containing the generated keys and related information.
* @property {string} privkey - The original private key.
* @property {string} nsec - The nsec value for the private key.
* @property {string} pubkey - The public key generated from the private key.
* @property {string} pubkeycompressed - The compressed public key generated from the private key.
* @property {string} npub - The npub value for the public key.
* @property {string} nrepo - The nrepo value for the public key.
* @property {string} taproot - The taproot address generated from the public key.
* @property {string} taproottestnet - The taproot testnet address generated from the public key.
* @property {string} liquidtaproot - The liquid taproot address generated from the public key.
* @property {string} ed25519pubkey - The ed25519 public key generated from the private key.
* @property {string} openSSHed25519pubkey - The OpenSSH ed25519 public key generated from the private key.
* @property {string} openSSHed25519privkey - The OpenSSH ed25519 private key generated from the private key.
*/
function getAllKeys(privateKey) {
const ed25519_prefix = '0000000b7373682d6564323535313900000020'
const ed25519_ssh_prefix = 'ssh-ed25519'
const taproot_prefix = 'bc1p'
const taproot_testnet_prefix = 'tb1p'
const liquid_prefix = 'ex1p'
const publicKey = getPublicKey(privateKey)
const npub = nip19(publicKey, 'npub')
const nrepo = nip19(publicKey, 'nrepo')
// console.log(npub)
// const ECPair = ECPairFactory(tiny)
// const keyPair = ECPair.fromPrivateKey(Buffer.from(privateKey, 'hex'))
// let wif = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey })
// let wifTestnet = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: TESTNET })
// console.log(keyPair.publicKey)
const ed25519pubkey = generate_public_key(privateKey)
// const p = tiny.pointFromScalar(Buffer.from(privateKey, 'hex'))
// var compressed = bufferToHex(p)
const compressed = getPublicKey(privateKey)
const privkeyPEM = encodePEM({ publicKey: ed25519pubkey, privateKey: privateKey })
const output = {
privkey: privateKey,
nsec: nip19(privateKey, 'nsec'),
pubkey: publicKey,
pubkeycompressed: compressed,
// bitcoinPubkey: wif.address,
// bitcoinTestnet3Pubkey: wifTestnet.address,
npub: npub,
nrepo: nrepo,
taproot: encodeBytes(taproot_prefix, publicKey),
taproottestnet: encodeBytes(taproot_testnet_prefix, publicKey),
liquidtaproot: encodeBytes(liquid_prefix, publicKey),
ed25519pubkey: ed25519pubkey,
openSSHed25519pubkey: `${ed25519_ssh_prefix} ${hexToBase64(
ed25519_prefix + ed25519pubkey
)}`,
openSSHed25519privkey: privkeyPEM
}
return output
}
export {
encodeBytes,
decodeBytes,
hexToBuffer,
bufferToHex,
generate_public_key,
hexToBase64,
secp256k1Add,
encodePEM,
decodePEM,
getAllKeys,
getPublicKey
}