import { createHmac, createPrivateKey, createPublicKey, timingSafeEqual, verify, sign } from 'node:crypto'; /** * คำนวณ HMAC-SHA256 จาก string หรือ JSON Object */ export function generateHmacSha256(data: string | object, secretKey: string): string { const payload = typeof data === 'string' ? data : JSON.stringify(data); return createHmac('sha256', secretKey) .update(payload, 'utf8') .digest('hex'); } /** * ตรวจสอบความถูกต้องของ HMAC (ใช้ timingSafeEqual เพื่อป้องกัน Timing Attacks) */ export function verifyHmacSha256(data: string | object, secretKey: string, expectedHmac: string): boolean { const calculatedHmac = generateHmacSha256(data, secretKey); const a = Buffer.from(calculatedHmac, 'hex'); const b = Buffer.from(expectedHmac, 'hex'); if (a.length !== b.length) { return false; } return timingSafeEqual(a, b); } // ========================================== // ตัวอย่างการใช้งาน // ========================================== /* const secret = 'my-super-secret-key'; const jsonData = { userId: 12345, action: 'transfer', amount: 500 }; // 1. สร้าง HMAC const hmacSignature = generateHmacSha256(jsonData, secret); console.log('HMAC Signature (Hex):', hmacSignature); // 2. ตรวจสอบ HMAC const isValid = verifyHmacSha256(jsonData, secret, hmacSignature); console.log('Is Signature Valid?:', isValid); // true */ /** * ฟังก์ชันสำหรับลงนามดิจิทัล (Sign) ข้อมูลด้วย Ed25519 Private Key (Hex) * @param message ข้อความดิบที่ต้องการเซ็น (string หรือ Buffer) * @param privateKeyHex คีย์ลับในรูปแบบ Hex String (ความยาว 64 ตัวอักษร) * @returns ลายเซ็น (Signature) ในรูปแบบ Hex String (ความยาว 128 ตัวอักษร) */ export function signDataEd25519(message: string | Buffer, privateKeyHex: string): string { const rawKeyBuffer = Buffer.from(privateKeyHex, 'hex'); // ตัดเอาเฉพาะ 32 bytes (Seed) กรณีที่ Hex ยาว 64 bytes (ที่ต่อกับ Public Key มา) const seed = rawKeyBuffer.length === 64 ? rawKeyBuffer.subarray(0, 32) : rawKeyBuffer; // PKCS#8 Header สำหรับ Ed25519 Private Key const pkcs8Header = Buffer.from('302e020100300506032b657004220420', 'hex'); const pkcs8Buffer = Buffer.concat([pkcs8Header, seed]); const privateKey = createPrivateKey({ key: pkcs8Buffer, format: 'der', type: 'pkcs8', }); const dataBuffer = typeof message === 'string' ? Buffer.from(message, 'utf-8') : message; const signatureBuffer = sign(null, dataBuffer, privateKey); return signatureBuffer.toString('hex'); } /** * ฟังก์ชันสำหรับตรวจสอบลายเซ็น (Verify) ด้วย Ed25519 Public Key (Hex) * @param message ข้อความดิบที่ใช้ในการเซ็น (string หรือ Buffer) * @param signatureHex ลายเซ็นในรูปแบบ Hex String * @param publicKeyHex คีย์สาธารณะในรูปแบบ Hex String (ความยาว 64 ตัวอักษร) * @returns ผลลัพธ์ความถูกต้อง (true = ผ่าน, false = ไม่ผ่าน) */ export function verifyDataEd25519(message: string | Buffer, signatureHex: string, publicKeyHex: string): boolean { try { const rawPublicKey = Buffer.from(publicKeyHex, 'hex'); // SPKI Header สำหรับ Ed25519 Public Key (32 bytes) const spkiHeader = Buffer.from('302a300506032b6570032100', 'hex'); const spkiBuffer = Buffer.concat([spkiHeader, rawPublicKey]); const publicKey = createPublicKey({ key: spkiBuffer, format: 'der', type: 'spki', }); const dataBuffer = typeof message === 'string' ? Buffer.from(message, 'utf-8') : message; const signatureBuffer = Buffer.from(signatureHex, 'hex'); return verify(null, dataBuffer, publicKey, signatureBuffer); } catch (error) { return false; } } /** * ฟังก์ชันสำหรับเรียงคีย์ของ Object ตามตัวอักษรเพื่อไม่ให้โครงสร้าง JSON เพี้ยน */ export function sortObjectKeys(obj: Record): Record { return Object.keys(obj) .sort() .reduce((result: Record, key: string) => { result[key] = obj[key]; return result; }, {}); }