@@ -12,9 +12,11 @@ namespace SharpCoreDB.Services;
1212/// </summary>
1313public class AesGcmEncryption : IDisposable
1414{
15- private readonly AesGcm ? _aesInstance ;
15+ private readonly AesGcm _aes ; // PERFORMANCE FIX: Single reusable AesGcm instance to avoid repeated allocations
1616 private readonly bool _disableEncrypt ;
1717 private readonly ArrayPool < byte > _pool = ArrayPool < byte > . Shared ;
18+ private readonly byte [ ] _nonceBuffer ; // PERFORMANCE FIX: Pre-allocated nonce buffer for reuse
19+ private readonly byte [ ] _tagBuffer ; // PERFORMANCE FIX: Pre-allocated tag buffer for reuse
1820
1921 /// <summary>
2022 /// Initializes a new instance of the AesGcmEncryption class.
@@ -26,7 +28,9 @@ public AesGcmEncryption(byte[] key, bool disableEncrypt = false)
2628 _disableEncrypt = disableEncrypt ;
2729 if ( ! disableEncrypt )
2830 {
29- _aesInstance = new AesGcm ( key , AesGcm . TagByteSizes . MaxSize ) ;
31+ _aes = new AesGcm ( key , AesGcm . TagByteSizes . MaxSize ) ; // PERFORMANCE FIX: Create once and reuse
32+ _nonceBuffer = _pool . Rent ( AesGcm . NonceByteSizes . MaxSize ) ; // PERFORMANCE FIX: Pre-allocate for reuse
33+ _tagBuffer = _pool . Rent ( AesGcm . TagByteSizes . MaxSize ) ; // PERFORMANCE FIX: Pre-allocate for reuse
3034 }
3135 }
3236
@@ -39,23 +43,19 @@ public byte[] Encrypt(byte[] data)
3943 {
4044 if ( _disableEncrypt ) return data ;
4145
42- var nonce = _pool . Rent ( AesGcm . NonceByteSizes . MaxSize ) ;
43- var tag = _pool . Rent ( AesGcm . TagByteSizes . MaxSize ) ;
4446 var cipher = _pool . Rent ( data . Length ) ;
4547 try
4648 {
47- RandomNumberGenerator . Fill ( nonce . AsSpan ( 0 , AesGcm . NonceByteSizes . MaxSize ) ) ;
48- _aesInstance ! . Encrypt ( nonce . AsSpan ( 0 , AesGcm . NonceByteSizes . MaxSize ) , data , cipher . AsSpan ( 0 , data . Length ) , tag . AsSpan ( 0 , AesGcm . TagByteSizes . MaxSize ) ) ;
49+ RandomNumberGenerator . Fill ( _nonceBuffer . AsSpan ( 0 , AesGcm . NonceByteSizes . MaxSize ) ) ; // PERFORMANCE FIX: Use pre-allocated buffer
50+ _aes . Encrypt ( _nonceBuffer . AsSpan ( 0 , AesGcm . NonceByteSizes . MaxSize ) , data , cipher . AsSpan ( 0 , data . Length ) , _tagBuffer . AsSpan ( 0 , AesGcm . TagByteSizes . MaxSize ) ) ; // PERFORMANCE FIX: Reuse AesGcm instance
4951 var result = new byte [ AesGcm . NonceByteSizes . MaxSize + data . Length + AesGcm . TagByteSizes . MaxSize ] ;
50- nonce . AsSpan ( 0 , AesGcm . NonceByteSizes . MaxSize ) . CopyTo ( result . AsSpan ( 0 , AesGcm . NonceByteSizes . MaxSize ) ) ;
52+ _nonceBuffer . AsSpan ( 0 , AesGcm . NonceByteSizes . MaxSize ) . CopyTo ( result . AsSpan ( 0 , AesGcm . NonceByteSizes . MaxSize ) ) ;
5153 cipher . AsSpan ( 0 , data . Length ) . CopyTo ( result . AsSpan ( AesGcm . NonceByteSizes . MaxSize , data . Length ) ) ;
52- tag . AsSpan ( 0 , AesGcm . TagByteSizes . MaxSize ) . CopyTo ( result . AsSpan ( AesGcm . NonceByteSizes . MaxSize + data . Length , AesGcm . TagByteSizes . MaxSize ) ) ;
54+ _tagBuffer . AsSpan ( 0 , AesGcm . TagByteSizes . MaxSize ) . CopyTo ( result . AsSpan ( AesGcm . NonceByteSizes . MaxSize + data . Length , AesGcm . TagByteSizes . MaxSize ) ) ;
5355 return result ;
5456 }
5557 finally
5658 {
57- _pool . Return ( nonce ) ;
58- _pool . Return ( tag ) ;
5959 _pool . Return ( cipher ) ;
6060 }
6161 }
@@ -76,7 +76,7 @@ public byte[] Decrypt(byte[] encryptedData)
7676 var nonce = encryptedData . AsSpan ( 0 , nonceSize ) ;
7777 var cipher = encryptedData . AsSpan ( nonceSize , cipherLength ) ;
7878 var tag = encryptedData . AsSpan ( nonceSize + cipherLength , tagSize ) ;
79- _aesInstance ! . Decrypt ( nonce , cipher , tag , plain ) ;
79+ _aes . Decrypt ( nonce , cipher , tag , plain ) ; // PERFORMANCE FIX: Reuse AesGcm instance
8080 return plain ;
8181 }
8282
@@ -99,22 +99,18 @@ public int Encrypt(ReadOnlySpan<byte> data, Span<byte> output)
9999 var totalSize = nonceSize + data . Length + tagSize ;
100100 if ( output . Length < totalSize ) throw new ArgumentException ( "Output buffer too small" ) ;
101101
102- var nonce = _pool . Rent ( nonceSize ) ;
103- var tag = _pool . Rent ( tagSize ) ;
104102 var cipher = _pool . Rent ( data . Length ) ;
105103 try
106104 {
107- RandomNumberGenerator . Fill ( nonce . AsSpan ( 0 , nonceSize ) ) ;
108- _aesInstance ! . Encrypt ( nonce . AsSpan ( 0 , nonceSize ) , data , cipher . AsSpan ( 0 , data . Length ) , tag . AsSpan ( 0 , tagSize ) ) ;
109- nonce . AsSpan ( 0 , nonceSize ) . CopyTo ( output . Slice ( 0 , nonceSize ) ) ;
105+ RandomNumberGenerator . Fill ( _nonceBuffer . AsSpan ( 0 , nonceSize ) ) ; // PERFORMANCE FIX: Use pre-allocated buffer
106+ _aes . Encrypt ( _nonceBuffer . AsSpan ( 0 , nonceSize ) , data , cipher . AsSpan ( 0 , data . Length ) , _tagBuffer . AsSpan ( 0 , tagSize ) ) ; // PERFORMANCE FIX: Reuse AesGcm instance
107+ _nonceBuffer . AsSpan ( 0 , nonceSize ) . CopyTo ( output . Slice ( 0 , nonceSize ) ) ;
110108 cipher . AsSpan ( 0 , data . Length ) . CopyTo ( output . Slice ( nonceSize , data . Length ) ) ;
111- tag . AsSpan ( 0 , tagSize ) . CopyTo ( output . Slice ( nonceSize + data . Length , tagSize ) ) ;
109+ _tagBuffer . AsSpan ( 0 , tagSize ) . CopyTo ( output . Slice ( nonceSize + data . Length , tagSize ) ) ;
112110 return totalSize ;
113111 }
114112 finally
115113 {
116- _pool . Return ( nonce ) ;
117- _pool . Return ( tag ) ;
118114 _pool . Return ( cipher ) ;
119115 }
120116 }
@@ -141,15 +137,51 @@ public int Decrypt(ReadOnlySpan<byte> encryptedData, Span<byte> output)
141137 var nonce = encryptedData . Slice ( 0 , nonceSize ) ;
142138 var cipher = encryptedData . Slice ( nonceSize , cipherLength ) ;
143139 var tag = encryptedData . Slice ( nonceSize + cipherLength , tagSize ) ;
144- _aesInstance ! . Decrypt ( nonce , cipher , tag , output . Slice ( 0 , cipherLength ) ) ;
140+ _aes . Decrypt ( nonce , cipher , tag , output . Slice ( 0 , cipherLength ) ) ; // PERFORMANCE FIX: Reuse AesGcm instance
145141 return cipherLength ;
146142 }
147143
144+ /// <summary>
145+ /// Encrypts a page using AES-256-GCM.
146+ /// </summary>
147+ /// <param name="page">The page data to encrypt (modified in place if buffer is large enough).</param>
148+ public void EncryptPage ( Span < byte > page )
149+ {
150+ var encrypted = Encrypt ( page . ToArray ( ) ) ;
151+ if ( page . Length >= encrypted . Length )
152+ {
153+ encrypted . AsSpan ( ) . CopyTo ( page ) ;
154+ }
155+ else
156+ {
157+ throw new ArgumentException ( "Page buffer too small for encrypted data" ) ;
158+ }
159+ }
160+
161+ /// <summary>
162+ /// Decrypts a page using AES-256-GCM.
163+ /// </summary>
164+ /// <param name="page">The encrypted page data (modified in place to decrypted data).</param>
165+ public void DecryptPage ( Span < byte > page )
166+ {
167+ var decrypted = Decrypt ( page . ToArray ( ) ) ;
168+ if ( page . Length >= decrypted . Length )
169+ {
170+ decrypted . AsSpan ( ) . CopyTo ( page ) ;
171+ }
172+ else
173+ {
174+ throw new ArgumentException ( "Page buffer too small for decrypted data" ) ;
175+ }
176+ }
177+
148178 /// <summary>
149179 /// Disposes the AesGcm instance.
150180 /// </summary>
151181 public void Dispose ( )
152182 {
153- _aesInstance ? . Dispose ( ) ;
183+ _aes ? . Dispose ( ) ;
184+ if ( _nonceBuffer != null ) _pool . Return ( _nonceBuffer ) ; // PERFORMANCE FIX: Return pre-allocated buffers
185+ if ( _tagBuffer != null ) _pool . Return ( _tagBuffer ) ;
154186 }
155187}
0 commit comments