@@ -2,45 +2,64 @@ use aes::cipher::Array;
22use aes:: cipher:: BlockModeEncrypt ;
33use aes:: cipher:: KeyIvInit ;
44use aes_gcm:: {
5- aead:: { Aead , KeyInit } ,
5+ aead:: { generic_array :: GenericArray , AeadInPlace , KeyInit } ,
66 Aes128Gcm , Key , Nonce ,
77} ;
88use inout:: block_padding:: Pkcs7 ;
99
10- pub ( crate ) fn encrypt ( plaintext : & [ u8 ] , key : & [ u8 ] , iv : & [ u8 ] , tag : & mut [ u8 ] ) -> Result < Vec < u8 > , aes_gcm:: Error > {
11- let key = Key :: < Aes128Gcm > :: from_slice ( key) ;
12- let nonce = Nonce :: from_slice ( iv) ;
13- let cipher = Aes128Gcm :: new ( key) ;
14-
15- // In OpenSSL, encrypting with GCM returns ciphertext usually without tag appended if you use `tag()` to retrieve it separate.
16- // aes-gcm crate append tag to ciphertext.
17- let mut ciphertext = cipher. encrypt ( nonce, plaintext) ?;
10+ /// An AES-128-GCM cipher cached across calls, keyed by the control stream's
11+ /// input key.
12+ ///
13+ /// The control stream encrypts feedback and decrypts input using
14+ /// `remote_input_key`, which only changes on key rotation (tracked by
15+ /// `remote_input_key_id`). Rebuilding the cipher — a full AES key schedule plus
16+ /// GHASH table — on every packet showed up on the per-input-event path, so we
17+ /// cache it and only rebuild when the key id changes.
18+ pub ( crate ) struct GcmCipher {
19+ cipher : Option < Aes128Gcm > ,
20+ key_id : i64 ,
21+ }
1822
19- // Split tag from ciphertext
20- let tag_len = 16 ;
21- let len = ciphertext. len ( ) ;
22- if len < tag_len {
23- return Err ( aes_gcm:: Error ) ;
23+ impl GcmCipher {
24+ pub fn new ( ) -> Self {
25+ Self {
26+ cipher : None ,
27+ key_id : i64:: MIN ,
28+ }
2429 }
25- let actual_ciphertext_len = len - tag_len;
26-
27- tag. copy_from_slice ( & ciphertext[ actual_ciphertext_len..] ) ;
28- ciphertext. truncate ( actual_ciphertext_len) ;
29-
30- Ok ( ciphertext)
31- }
3230
33- pub ( crate ) fn decrypt ( ciphertext : & [ u8 ] , key : & [ u8 ] , iv : & [ u8 ] , tag : & [ u8 ] ) -> Result < Vec < u8 > , aes_gcm:: Error > {
34- let key = Key :: < Aes128Gcm > :: from_slice ( key) ;
35- let nonce = Nonce :: from_slice ( iv) ;
36- let cipher = Aes128Gcm :: new ( key) ;
31+ /// Return the cached cipher, rebuilding it if the key has rotated.
32+ fn get ( & mut self , key : & [ u8 ] , key_id : i64 ) -> Result < & Aes128Gcm , ( ) > {
33+ if self . cipher . is_none ( ) || self . key_id != key_id {
34+ if key. len ( ) != 16 {
35+ tracing:: warn!( "Control key must be 16 bytes, got {}." , key. len( ) ) ;
36+ self . cipher = None ;
37+ return Err ( ( ) ) ;
38+ }
39+ self . cipher = Some ( Aes128Gcm :: new ( Key :: < Aes128Gcm > :: from_slice ( key) ) ) ;
40+ self . key_id = key_id;
41+ }
42+ self . cipher . as_ref ( ) . ok_or ( ( ) )
43+ }
3744
38- // Append tag to ciphertext for aes-gcm crate
39- let mut payload = Vec :: with_capacity ( ciphertext. len ( ) + tag. len ( ) ) ;
40- payload. extend_from_slice ( ciphertext) ;
41- payload. extend_from_slice ( tag) ;
45+ /// Encrypt `buffer` in place, returning the detached 16-byte tag.
46+ pub fn encrypt ( & mut self , key : & [ u8 ] , key_id : i64 , iv : & [ u8 ] , buffer : & mut [ u8 ] ) -> Result < [ u8 ; 16 ] , ( ) > {
47+ let cipher = self . get ( key, key_id) ?;
48+ let tag = cipher
49+ . encrypt_in_place_detached ( Nonce :: from_slice ( iv) , b"" , buffer)
50+ . map_err ( |e| tracing:: warn!( "Failed to encrypt control data: {e}" ) ) ?;
51+ let mut out = [ 0u8 ; 16 ] ;
52+ out. copy_from_slice ( & tag) ;
53+ Ok ( out)
54+ }
4255
43- cipher. decrypt ( nonce, payload. as_ref ( ) )
56+ /// Decrypt `buffer` in place using the detached 16-byte tag.
57+ pub fn decrypt ( & mut self , key : & [ u8 ] , key_id : i64 , iv : & [ u8 ] , tag : & [ u8 ; 16 ] , buffer : & mut [ u8 ] ) -> Result < ( ) , ( ) > {
58+ let cipher = self . get ( key, key_id) ?;
59+ cipher
60+ . decrypt_in_place_detached ( Nonce :: from_slice ( iv) , b"" , buffer, GenericArray :: from_slice ( tag) )
61+ . map_err ( |e| tracing:: warn!( "Failed to decrypt control message: {e}" ) )
62+ }
4463}
4564
4665pub ( crate ) fn encrypt_cbc ( data : & [ u8 ] , key : & [ u8 ] , iv : & [ u8 ] ) -> Result < Vec < u8 > , String > {
0 commit comments