UNPKG

@toruslabs/metadata-helpers

Version:
143 lines (139 loc) 5.6 kB
'use strict'; var modular_js = require('@noble/curves/abstract/modular.js'); var ed25519_js = require('@noble/curves/ed25519.js'); var secp256k1_js = require('@noble/curves/secp256k1.js'); var utils_js = require('@noble/curves/utils.js'); var sha2_js = require('@noble/hashes/sha2.js'); var sha3_js = require('@noble/hashes/sha3.js'); var base = require('@scure/base'); var hex = require('./hex.js'); var number = require('./number.js'); const getSecp256k1 = () => secp256k1_js.secp256k1; const getEd25519 = () => ed25519_js.ed25519; const getKeyCurve = keyType => { if (keyType === "secp256k1") return getSecp256k1(); if (keyType === "ed25519") return getEd25519(); throw new Error(`Invalid keyType: ${keyType}`); }; function derivePubKey(ecCurve, sk) { return ecCurve.Point.BASE.multiply(sk).toAffine(); } function generatePrivateKey(ecCurveOrKeyType) { const ec = typeof ecCurveOrKeyType === "string" ? getKeyCurve(ecCurveOrKeyType) : ecCurveOrKeyType; return ec.utils.randomSecretKey(); } /** * Returns keccak256 hash as hex string (torus.js–compatible name). By default 0x-prefixed. * Use keccak256Bytes when you need raw bytes. * * @param a - Input bytes. * @param options - Optional. Set `{ prefixed: false }` to omit "0x" prefix. */ function keccak256(a, options) { const hash = utils_js.bytesToHex(sha3_js.keccak_256(a)); return (options === null || options === void 0 ? void 0 : options.prefixed) === false ? hash : `0x${hash}`; } /** Returns keccak256 hash as raw bytes. */ function keccak256Bytes(a) { return sha3_js.keccak_256(a); } function adjustScalarBytes(bytes) { // Section 5: For X25519, in order to decode 32 random bytes as an integer scalar, // set the three least significant bits of the first byte bytes[0] &= 248; // 0b1111_1000 // and the most significant bit of the last to zero, bytes[31] &= 127; // 0b0111_1111 // set the second most significant bit of the last byte to 1 bytes[31] |= 64; // 0b0100_0000 return bytes; } /** Ed25519 extended public key derivation per RFC 8032 5.1.5 */ function getEd25519ExtendedPublicKey(keyBytes) { const ed25519Curve = getKeyCurve("ed25519"); const N = ed25519Curve.Point.CURVE().n; if (keyBytes.length !== 32) { throw new Error("Invalid seed for ed25519 key derivation"); } const hashed = sha2_js.sha512(keyBytes); if (hashed.length !== 64) { throw new Error("Invalid hash length for ed25519 seed"); } const head = utils_js.bytesToNumberLE(adjustScalarBytes(new Uint8Array(hashed.slice(0, 32)))); const scalar = modular_js.mod(head, N); const point = derivePubKey(ed25519Curve, scalar); return { scalar, point }; } function encodeEd25519Point(point) { const ed25519Curve = getKeyCurve("ed25519"); return ed25519Curve.Point.fromAffine(point).toBytes(); } /** Derives a secp256k1 key pair from an Ed25519 scalar via keccak hash. */ const getSecpKeyFromEd25519 = ed25519Scalar => { const secp = getSecp256k1(); const N = secp.Point.CURVE().n; const keyHash = sha3_js.keccak_256(utils_js.numberToBytesBE(ed25519Scalar, 32)); const secpScalar = modular_js.mod(utils_js.bytesToNumberBE(keyHash), N); const point = derivePubKey(secp, secpScalar); return { scalar: secpScalar, point }; }; /** Encodes a secp256k1 affine point as 64-byte uncompressed key (without 04 prefix). */ function getSecp256k1PublicKeyFromAffinePoint(point) { const uncompressed = getSecp256k1().Point.fromAffine(point).toBytes(false); return uncompressed.slice(1); } function generateAddressFromPoint(keyType, point) { if (keyType === "secp256k1") { const publicKey = getSecp256k1PublicKeyFromAffinePoint(point); const evmAddressLower = `0x${keccak256(publicKey).slice(64 - 38)}`; return hex.getChecksumAddress(evmAddressLower); } else if (keyType === "ed25519") { const publicKey = encodeEd25519Point(point); return base.base58.encode(publicKey); } throw new Error(`Invalid keyType: ${keyType}`); } function generateAddressFromPrivKey(keyType, privateKey) { const ecCurve = getKeyCurve(keyType); const point = derivePubKey(ecCurve, privateKey); return generateAddressFromPoint(keyType, point); } function generateAddressFromPubKey(keyType, publicKeyX, publicKeyY) { return generateAddressFromPoint(keyType, { x: publicKeyX, y: publicKeyY }); } /** * Derive postbox key: privKey - nonce (mod n). * Inputs accept hex with or without 0x prefix. * By default returns raw hex (no prefix) for backward compat. * * @param options - Optional. Set `{ prefixed: true }` to add "0x" prefix. */ function getPostboxKeyFrom1OutOf1(ecCurve, privKey, nonce, options) { const privKeyBI = number.toBigIntBE(privKey); const nonceBI = number.toBigIntBE(nonce); const n = ecCurve.Point.CURVE().n; const result = modular_js.mod(privKeyBI - nonceBI, n); return number.bigintToHex(result, 64, options); } exports.derivePubKey = derivePubKey; exports.encodeEd25519Point = encodeEd25519Point; exports.generateAddressFromPrivKey = generateAddressFromPrivKey; exports.generateAddressFromPubKey = generateAddressFromPubKey; exports.generatePrivateKey = generatePrivateKey; exports.getEd25519 = getEd25519; exports.getEd25519ExtendedPublicKey = getEd25519ExtendedPublicKey; exports.getKeyCurve = getKeyCurve; exports.getPostboxKeyFrom1OutOf1 = getPostboxKeyFrom1OutOf1; exports.getSecp256k1 = getSecp256k1; exports.getSecp256k1PublicKeyFromAffinePoint = getSecp256k1PublicKeyFromAffinePoint; exports.getSecpKeyFromEd25519 = getSecpKeyFromEd25519; exports.keccak256 = keccak256; exports.keccak256Bytes = keccak256Bytes;