import { keyPairFromSeed, keyPairFromSecretKey, sign, signVerify, KeyPair, getSecureRandomBytes } from '@ton/crypto'; import nacl from 'tweetnacl'; const ed2curve = require('ed2curve'); import { client, getKeypair } from '../toolchain'; import { Content } from '../../build/Gift/Gift_Gift'; import { Address } from '@ton/core'; import { WalletContractV4 } from '@ton/ton'; /** * Converts a bigint (uint256) to Buffer (32 bytes, big-endian). * @param bigIntValue The bigint value to convert. * @param byteLength The byte length (default 32 for uint256). * @returns Buffer The converted byte buffer. */ function bigIntToBuffer(bigIntValue: bigint, byteLength: number = 32): Buffer { const buffer = Buffer.alloc(byteLength); for (let i = 0; i < byteLength; i++) { buffer[byteLength - 1 - i] = Number((bigIntValue >> BigInt(i * 8)) & 0xffn); } return buffer; } /** * Get the public key of the recipient wallet. * @param address The recipient's TON address. * @returns Promise The recipient's public key as Buffer. */ export async function getRecipientPublicKey(address: Address): Promise { console.log(`Fetching public key for: ${address.toString()}`); return client.callGetMethod(address, 'get_public_key') .then((result) => { return bigIntToBuffer(result.stack.readBigNumber()); }) .catch((error) => { console.error('Error fetching recipient public key:', error); throw error; }); } /** * Encrypt a Content message using sender's private key and recipient's public key. * @param content The Content to encrypt. * @param senderSecretKey The sender's secret key. * @param recipientPublicKey The recipient's public key. * @returns Promise The encrypted message data (nonce + encrypted). */ export async function encryptMessage( content: Content, senderSecretKey: Buffer, recipientPublicKey: Buffer, ): Promise { // Serialize Content to JSON string const contentString = JSON.stringify(content); const contentBuffer = new Uint8Array(Buffer.from(contentString, 'utf-8')); // Convert Ed25519 keys to Curve25519 const senderSecretCurve = ed2curve.convertSecretKey(senderSecretKey); const recipientPublicCurve = ed2curve.convertPublicKey(recipientPublicKey); if (!senderSecretCurve || !recipientPublicCurve) { throw new Error('Invalid key conversion'); } // Generate a random nonce const nonceBuffer = await getSecureRandomBytes(24); const nonce = new Uint8Array(nonceBuffer); // Encrypt using NaCl box const encrypted = nacl.box(contentBuffer, nonce, recipientPublicCurve, senderSecretCurve); // Return nonce + encrypted const combined = new Uint8Array(nonce.length + encrypted.length); combined.set(nonce, 0); combined.set(encrypted, nonce.length); return combined; } /** * Decrypt an encrypted message using recipient's private key and sender's public key. * @param encryptedData The encrypted data (nonce + encrypted). * @param recipientSecretKey The recipient's secret key. * @param senderPublicKey The sender's public key. * @returns Promise The decrypted Content. */ export async function decryptMessage( encryptedData: Uint8Array, recipientSecretKey: Buffer, senderPublicKey: Buffer, ): Promise { // Extract nonce (first 24 bytes) const nonce = encryptedData.slice(0, 24); const encrypted = encryptedData.slice(24); // Convert Ed25519 keys to Curve25519 const recipientSecretCurve = ed2curve.convertSecretKey(recipientSecretKey); const senderPublicCurve = ed2curve.convertPublicKey(senderPublicKey); if (!recipientSecretCurve || !senderPublicCurve) { throw new Error('Invalid key conversion'); } // Decrypt using NaCl box open const decrypted = nacl.box.open(encrypted, nonce, senderPublicCurve, recipientSecretCurve); if (!decrypted) { throw new Error('Decryption failed'); } const contentString = new Buffer(decrypted).toString('utf-8'); const content: Content = JSON.parse(contentString); return content; } /** * Encrypt a string content using sender's private key and recipient's public key. * @param senderSecretKey The sender's secret key. * @param recipientPublicKey The recipient's public key. * @param content The string to encrypt. * @returns Promise The encrypted data. */ export async function encryptContent( senderSecretKey: Buffer, recipientPublicKey: Buffer, content: string, ): Promise { const contentBuffer = new Uint8Array(Buffer.from(content, 'utf-8')); // Convert Ed25519 keys to Curve25519 const senderSecretCurve = ed2curve.convertSecretKey(senderSecretKey); const recipientPublicCurve = ed2curve.convertPublicKey(recipientPublicKey); if (!senderSecretCurve || !recipientPublicCurve) { throw new Error('Invalid key conversion'); } const nonceBuffer = await getSecureRandomBytes(24); const nonce = new Uint8Array(nonceBuffer); const encrypted = nacl.box(contentBuffer, nonce, recipientPublicCurve, senderSecretCurve); const combined = new Uint8Array(nonce.length + encrypted.length); combined.set(nonce, 0); combined.set(encrypted, nonce.length); return combined; } /** * Decrypt encrypted content using recipient's private key and sender's public key. * @param recipientSecretKey The recipient's secret key. * @param senderPublicKey The sender's public key. * @param encrypted The encrypted data. * @returns Promise The decrypted string. */ export async function decryptContent( recipientSecretKey: Buffer, senderPublicKey: Buffer, encrypted: Uint8Array, ): Promise { // Convert Ed25519 keys to Curve25519 const recipientSecretCurve = ed2curve.convertSecretKey(recipientSecretKey); const senderPublicCurve = ed2curve.convertPublicKey(senderPublicKey); if (!recipientSecretCurve || !senderPublicCurve) { throw new Error('Invalid key conversion'); } const nonce = encrypted.slice(0, 24); const encryptedPart = encrypted.slice(24); const decrypted = nacl.box.open(encryptedPart, nonce, senderPublicCurve, recipientSecretCurve); if (!decrypted) { throw new Error('Decryption failed'); } return new Buffer(decrypted).toString('utf-8'); }