169 lines
6.3 KiB
TypeScript
169 lines
6.3 KiB
TypeScript
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<Buffer> The recipient's public key as Buffer.
|
|
*/
|
|
export async function getRecipientPublicKey(address: Address): Promise<Buffer> {
|
|
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<Uint8Array> The encrypted message data (nonce + encrypted).
|
|
*/
|
|
export async function encryptMessage(
|
|
content: Content,
|
|
senderSecretKey: Buffer,
|
|
recipientPublicKey: Buffer,
|
|
): Promise<Uint8Array> {
|
|
// 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<Content> The decrypted Content.
|
|
*/
|
|
export async function decryptMessage(
|
|
encryptedData: Uint8Array,
|
|
recipientSecretKey: Buffer,
|
|
senderPublicKey: Buffer,
|
|
): Promise<Content> {
|
|
// 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<Uint8Array> The encrypted data.
|
|
*/
|
|
export async function encryptContent(
|
|
senderSecretKey: Buffer,
|
|
recipientPublicKey: Buffer,
|
|
content: string,
|
|
): Promise<Uint8Array> {
|
|
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<string> The decrypted string.
|
|
*/
|
|
export async function decryptContent(
|
|
recipientSecretKey: Buffer,
|
|
senderPublicKey: Buffer,
|
|
encrypted: Uint8Array,
|
|
): Promise<string> {
|
|
// 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');
|
|
}
|