Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206// Tezos Crypto Library for Keeps Wallet// Pure JS implementation - no WASM (Chrome extension compatible)
import * as bip39 from 'bip39';import nacl from 'tweetnacl';import { encodeBase64, decodeBase64, encodeUTF8, decodeUTF8 } from 'tweetnacl-util';import { scrypt } from 'scrypt-js';import { b58cencode, b58cdecode, prefix } from '@taquito/utils';import { blake2b as blake2bHash } from 'blakejs';
// Crypto is ready immediately (no WASM to load)export async function initCrypto() { // No-op for pure JS implementation return true;}
// Generate a new 24-word mnemonicexport function generateMnemonic() { return bip39.generateMnemonic(256); // 256 bits = 24 words}
// Validate a mnemonic (supports 12 or 24 words)export function validateMnemonic(mnemonic) { return bip39.validateMnemonic(mnemonic);}
// Blake2b hash — proper implementation for Tezos address derivationfunction blake2b(data, outlen = 32) { return blake2bHash(data, null, outlen);}
// Derive Tezos keypair from mnemonicexport async function deriveKeypair(mnemonic, password = '') { // Get seed from mnemonic (with optional passphrase) const seed = await bip39.mnemonicToSeed(mnemonic, password); // Tezos uses the first 32 bytes of the seed for ed25519 const seedBytes = new Uint8Array(seed.slice(0, 32)); // Generate ed25519 keypair from seed const keypair = nacl.sign.keyPair.fromSeed(seedBytes); // For Tezos address, we need Blake2b-160 of the public key // Using simplified hash for now const publicKeyHash = blake2b(keypair.publicKey, 20); return { privateKey: b58cencode(seedBytes, prefix.edsk2), // 32-byte seed publicKey: b58cencode(keypair.publicKey, prefix.edpk), address: b58cencode(publicKeyHash, prefix.tz1), secretKeyBytes: keypair.secretKey, // Full 64-byte secret key for signing publicKeyBytes: keypair.publicKey, };}
// Import keypair from private key (edsk...)export async function importFromPrivateKey(privateKeyB58) { let seedBytes; if (privateKeyB58.length === 98) { // Full 64-byte secret key (edsk prefix) const decoded = b58cdecode(privateKeyB58, prefix.edsk); seedBytes = decoded.slice(0, 32); // First 32 bytes are the seed } else if (privateKeyB58.length === 54) { // 32-byte seed only (edsk2 prefix) seedBytes = b58cdecode(privateKeyB58, prefix.edsk2); } else { throw new Error('Invalid private key format. Expected edsk... key.'); } // Generate ed25519 keypair from seed const keypair = nacl.sign.keyPair.fromSeed(seedBytes); // Compute address const publicKeyHash = blake2b(keypair.publicKey, 20); return { privateKey: b58cencode(seedBytes, prefix.edsk2), publicKey: b58cencode(keypair.publicKey, prefix.edpk), address: b58cencode(publicKeyHash, prefix.tz1), secretKeyBytes: keypair.secretKey, publicKeyBytes: keypair.publicKey, };}
// Derive encryption key from password using scryptasync function deriveKey(password, salt) { const passwordBytes = decodeUTF8(password); const N = 16384; // CPU/memory cost parameter const r = 8; // Block size const p = 1; // Parallelization const dkLen = nacl.secretbox.keyLength; // 32 bytes const key = await scrypt(passwordBytes, salt, N, r, p, dkLen); return new Uint8Array(key);}
// Encrypt data with password using scrypt + NaCl secretboxexport async function encrypt(data, password) { // Generate random salt and nonce const salt = nacl.randomBytes(32); const nonce = nacl.randomBytes(nacl.secretbox.nonceLength); // 24 bytes // Derive key from password const key = await deriveKey(password, salt); // Encrypt with NaCl secretbox (XSalsa20-Poly1305) const dataBytes = typeof data === 'string' ? decodeUTF8(data) : new Uint8Array(data); const ciphertext = nacl.secretbox(dataBytes, nonce, key); // Combine salt + nonce + ciphertext const combined = new Uint8Array(salt.length + nonce.length + ciphertext.length); combined.set(salt, 0); combined.set(nonce, salt.length); combined.set(ciphertext, salt.length + nonce.length); // Return as base64 return encodeBase64(combined);}
// Decrypt data with passwordexport async function decrypt(encryptedBase64, password) { const combined = decodeBase64(encryptedBase64); // Extract salt, nonce, ciphertext const salt = combined.slice(0, 32); const nonce = combined.slice(32, 32 + nacl.secretbox.nonceLength); const ciphertext = combined.slice(32 + nacl.secretbox.nonceLength); // Derive key from password const key = await deriveKey(password, salt); // Decrypt const decrypted = nacl.secretbox.open(ciphertext, nonce, key); if (!decrypted) { throw new Error('Decryption failed - invalid password'); } return encodeUTF8(decrypted);}
// Sign a message with private key bytesexport async function sign(message, secretKeyBytes) { const messageBytes = typeof message === 'string' ? hexToBytes(message) : new Uint8Array(message); const signature = nacl.sign.detached(messageBytes, secretKeyBytes); return { bytes: signature, hex: bytesToHex(signature), sig: b58cencode(signature, prefix.sig), edsig: b58cencode(signature, prefix.edsig), };}
// Sign a Tezos operation (forged bytes)export async function signOperation(forgedBytes, secretKeyBytes) { // Tezos operations are signed with a watermark prefix (0x03 for operations) const watermark = new Uint8Array([3]); const bytes = hexToBytes(forgedBytes); // Combine watermark + bytes and hash with Blake2b const toSign = new Uint8Array(watermark.length + bytes.length); toSign.set(watermark, 0); toSign.set(bytes, watermark.length); const hash = blake2b(toSign, 32); // Sign the hash const signature = nacl.sign.detached(hash, secretKeyBytes); return { bytes: signature, hex: bytesToHex(signature), sig: b58cencode(signature, prefix.sig), edsig: b58cencode(signature, prefix.edsig), signedBytes: forgedBytes + bytesToHex(signature), };}
// Clear sensitive data from memory (best effort)export function clearSensitiveData(data) { if (data instanceof Uint8Array) { data.fill(0); } return null;}
// Helper: hex string to bytesfunction hexToBytes(hex) { const bytes = new Uint8Array(hex.length / 2); for (let i = 0; i < bytes.length; i++) { bytes[i] = parseInt(hex.substr(i * 2, 2), 16); } return bytes;}
// Helper: bytes to hex stringfunction bytesToHex(bytes) { return Array.from(bytes) .map(b => b.toString(16).padStart(2, '0')) .join('');}