11import * as crypto from 'node:crypto' ;
22
33/**
4- * EncryptionService - Handles ECIES-like encryption for agent data.
4+ * EncryptionService - Handles ECIES encryption for agent data.
55 * Uses X25519 for Key Exchange and AES-256-GCM for symmetric encryption.
66 */
77export class EncryptionService {
8+ private static X25519_SPKI_HEADER = Buffer . from ( '302a300506032b656e032100' , 'hex' ) ;
9+ private static X25519_PKCS8_HEADER = Buffer . from ( '302e020100300506032b656e04220420' , 'hex' ) ;
10+
811 /**
912 * Encrypts data for a specific public key (X25519).
1013 * @param data The data to encrypt (Buffer or string).
11- * @param recipientPublicKey The recipient's X25519 public key.
14+ * @param recipientPublicKey The recipient's X25519 public key (raw 32 bytes) .
1215 * @returns The encrypted payload (JSON with iv, ciphertext, ephemeralPublicKey, tag).
1316 */
1417 static async encrypt ( data : Buffer | string , recipientPublicKey : Buffer ) {
15- // 1. Generate ephemeral key pair
16- const ephemeral = crypto . createECDH ( 'prime256v1' ) ;
17- ephemeral . generateKeys ( ) ;
18+ // 1. Generate ephemeral X25519 key pair
19+ const { publicKey : ephemeralPub , privateKey : ephemeralPriv } = crypto . generateKeyPairSync ( 'x25519' ) ;
1820
19- // 2. Perform Diffie-Hellman to get shared secret
20- const sharedSecret = ephemeral . computeSecret ( recipientPublicKey ) ;
21+ // 2. Wrap recipient's raw public key in SPKI
22+ const remotePubKey = crypto . createPublicKey ( {
23+ key : Buffer . concat ( [ this . X25519_SPKI_HEADER , recipientPublicKey ] ) ,
24+ format : 'der' ,
25+ type : 'spki'
26+ } ) ;
27+
28+ // 3. Perform Diffie-Hellman to get shared secret
29+ const sharedSecret = crypto . diffieHellman ( {
30+ privateKey : ephemeralPriv ,
31+ publicKey : remotePubKey
32+ } ) ;
2133
22- // 3 . Derive symmetric key (AES-256) from shared secret using HKDF
34+ // 4 . Derive symmetric key (AES-256) using HKDF
2335 const salt = crypto . randomBytes ( 16 ) ;
24- const encryptionKey = crypto . hkdfSync ( 'sha256' , sharedSecret , salt , Buffer . from ( 'agent-db-encryption ' ) , 32 ) ;
36+ const encryptionKey = crypto . hkdfSync ( 'sha256' , sharedSecret , salt , Buffer . from ( 'agent-db-x25519-v1 ' ) , 32 ) ;
2537
26- // 4 . Encrypt with AES-256-GCM
38+ // 5 . Encrypt with AES-256-GCM
2739 const iv = crypto . randomBytes ( 12 ) ;
2840 const cipher = crypto . createCipheriv ( 'aes-256-gcm' , Buffer . from ( encryptionKey ) , iv ) ;
2941
@@ -32,37 +44,51 @@ export class EncryptionService {
3244 ciphertext = Buffer . concat ( [ ciphertext , cipher . final ( ) ] ) ;
3345 const tag = cipher . getAuthTag ( ) ;
3446
47+ const ephemRaw = ephemeralPub . export ( { format : 'der' , type : 'spki' } ) . slice ( this . X25519_SPKI_HEADER . length ) ;
48+
3549 return {
3650 iv : iv . toString ( 'hex' ) ,
3751 tag : tag . toString ( 'hex' ) ,
3852 salt : salt . toString ( 'hex' ) ,
39- ephemeralPublicKey : ephemeral . getPublicKey ( ) . toString ( 'hex' ) ,
53+ ephemeralPublicKey : ephemRaw . toString ( 'hex' ) ,
4054 ciphertext : ciphertext . toString ( 'hex' )
4155 } ;
4256 }
4357
4458 /**
4559 * Decrypts an ECIES payload using a raw private key.
4660 * @param payload The encrypted payload.
47- * @param privateKey The recipient's raw private key Buffer.
61+ * @param privateKey The recipient's raw private key Buffer (32 bytes) .
4862 * @returns The decrypted data as a Buffer.
4963 */
5064 static async decrypt ( payload : any , privateKey : Buffer ) {
5165 const { iv, tag, salt, ephemeralPublicKey, ciphertext } = payload ;
5266
53- // 1. Reconstruct ECDH state
54- const ecdh = crypto . createECDH ( 'prime256v1' ) ;
55- ecdh . setPrivateKey ( privateKey ) ;
67+ // 1. Wrap raw keys in KeyObjects
68+ const localPrivKey = crypto . createPrivateKey ( {
69+ key : Buffer . concat ( [ this . X25519_PKCS8_HEADER , privateKey ] ) ,
70+ format : 'der' ,
71+ type : 'pkcs8'
72+ } ) ;
73+
74+ const remotePubKey = crypto . createPublicKey ( {
75+ key : Buffer . concat ( [ this . X25519_SPKI_HEADER , Buffer . from ( ephemeralPublicKey , 'hex' ) ] ) ,
76+ format : 'der' ,
77+ type : 'spki'
78+ } ) ;
5679
5780 // 2. Perform Diffie-Hellman for shared secret
58- const sharedSecret = ecdh . computeSecret ( Buffer . from ( ephemeralPublicKey , 'hex' ) ) ;
81+ const sharedSecret = crypto . diffieHellman ( {
82+ privateKey : localPrivKey ,
83+ publicKey : remotePubKey
84+ } ) ;
5985
6086 // 3. Derive symmetric key using HKDF
6187 const encryptionKey = crypto . hkdfSync (
6288 'sha256' ,
6389 sharedSecret ,
6490 Buffer . from ( salt , 'hex' ) ,
65- Buffer . from ( 'agent-db-encryption ' ) ,
91+ Buffer . from ( 'agent-db-x25519-v1 ' ) ,
6692 32
6793 ) ;
6894
0 commit comments