@@ -14,36 +14,18 @@ namespace SharpCoreDB.Services;
1414/// SECURITY: All sensitive buffers are cleared immediately after use.
1515/// PERFORMANCE: Eliminates all unnecessary allocations through Span<byte> and stackalloc.
1616/// </summary>
17- public class AesGcmEncryption : IDisposable
17+ /// <param name="key">The encryption key (must be 32 bytes for AES-256).</param>
18+ /// <param name="disableEncrypt">If true, encryption is disabled (passthrough mode).</param>
19+ public sealed class AesGcmEncryption ( byte [ ] key , bool disableEncrypt = false ) : IDisposable
1820{
19- private readonly byte [ ] _key ;
20- private readonly bool _disableEncrypt ;
21+ private readonly byte [ ] _key = disableEncrypt ? [ ] : [ .. key ] ;
2122 private readonly ArrayPool < byte > _pool = ArrayPool < byte > . Shared ;
2223
2324 // Size constants for AES-GCM
2425 private const int NonceSize = 12 ; // AesGcm.NonceByteSizes.MaxSize = 12
2526 private const int TagSize = 16 ; // AesGcm.TagByteSizes.MaxSize = 16
2627 private const int StackAllocThreshold = 256 ; // Use stackalloc for buffers <= 256 bytes
2728
28- /// <summary>
29- /// Initializes a new instance of the <see cref="AesGcmEncryption"/> class.
30- /// </summary>
31- /// <param name="key">The encryption key (must be 32 bytes for AES-256).</param>
32- /// <param name="disableEncrypt">If true, encryption is disabled (passthrough mode).</param>
33- public AesGcmEncryption ( byte [ ] key , bool disableEncrypt = false )
34- {
35- _disableEncrypt = disableEncrypt ;
36- if ( ! disableEncrypt )
37- {
38- _key = new byte [ key . Length ] ;
39- key . CopyTo ( _key , 0 ) ;
40- }
41- else
42- {
43- _key = Array . Empty < byte > ( ) ;
44- }
45- }
46-
4729 /// <summary>
4830 /// Encrypts data using AES-256-GCM with optimized buffer handling.
4931 /// Uses stackalloc for nonce/tag, ArrayPool for cipher.
@@ -53,7 +35,7 @@ public AesGcmEncryption(byte[] key, bool disableEncrypt = false)
5335 [ MethodImpl ( MethodImplOptions . AggressiveOptimization ) ]
5436 public byte [ ] Encrypt ( byte [ ] data )
5537 {
56- if ( _disableEncrypt )
38+ if ( disableEncrypt )
5739 return data ;
5840
5941 using var aes = new AesGcm ( _key , TagSize ) ;
@@ -71,7 +53,7 @@ public byte[] Encrypt(byte[] data)
7153 cipherArray = _pool . Rent ( data . Length ) ;
7254 Span < byte > cipher = cipherArray . AsSpan ( 0 , data . Length ) ;
7355
74- // Encrypt: plaintext → ciphertext + tag
56+ // Encrypt: plaintext → ciphertext + tag
7557 aes . Encrypt ( nonce , data , cipher , tag ) ;
7658
7759 // Build result: [nonce][cipher][tag]
@@ -86,9 +68,7 @@ public byte[] Encrypt(byte[] data)
8668 {
8769 // SECURITY: Clear sensitive cipher data
8870 if ( cipherArray != null )
89- {
9071 _pool . Return ( cipherArray , clearArray : true ) ;
91- }
9272
9373 // SECURITY: Clear stack-allocated buffers
9474 nonce . Clear ( ) ;
@@ -104,7 +84,7 @@ public byte[] Encrypt(byte[] data)
10484 [ MethodImpl ( MethodImplOptions . AggressiveOptimization ) ]
10585 public byte [ ] Decrypt ( byte [ ] encryptedData )
10686 {
107- if ( _disableEncrypt )
87+ if ( disableEncrypt )
10888 return encryptedData ;
10989
11090 var cipherLength = encryptedData . Length - NonceSize - TagSize ;
@@ -134,7 +114,7 @@ public byte[] Decrypt(byte[] encryptedData)
134114 [ MethodImpl ( MethodImplOptions . AggressiveOptimization ) ]
135115 public int Encrypt ( ReadOnlySpan < byte > data , Span < byte > output )
136116 {
137- if ( _disableEncrypt )
117+ if ( disableEncrypt )
138118 {
139119 data . CopyTo ( output ) ;
140120 return data . Length ;
@@ -166,8 +146,8 @@ public int Encrypt(ReadOnlySpan<byte> data, Span<byte> output)
166146
167147 // Write to output: [nonce][cipher][tag]
168148 nonce . CopyTo ( output ) ;
169- cipher . CopyTo ( output . Slice ( NonceSize ) ) ;
170- tag . CopyTo ( output . Slice ( NonceSize + data . Length ) ) ;
149+ cipher . CopyTo ( output [ NonceSize .. ] ) ;
150+ tag . CopyTo ( output [ ( NonceSize + data . Length ) .. ] ) ;
171151
172152 // SECURITY: Clear stack buffers
173153 cipher . Clear ( ) ;
@@ -183,8 +163,8 @@ public int Encrypt(ReadOnlySpan<byte> data, Span<byte> output)
183163
184164 // Write to output: [nonce][cipher][tag]
185165 nonce . CopyTo ( output ) ;
186- cipher . CopyTo ( output . Slice ( NonceSize ) ) ;
187- tag . CopyTo ( output . Slice ( NonceSize + data . Length ) ) ;
166+ cipher . CopyTo ( output [ NonceSize .. ] ) ;
167+ tag . CopyTo ( output [ ( NonceSize + data . Length ) .. ] ) ;
188168 }
189169
190170 return totalSize ;
@@ -193,9 +173,7 @@ public int Encrypt(ReadOnlySpan<byte> data, Span<byte> output)
193173 {
194174 // SECURITY: Clear pooled buffer
195175 if ( cipherArray != null )
196- {
197176 _pool . Return ( cipherArray , clearArray : true ) ;
198- }
199177
200178 // SECURITY: Clear stack-allocated buffers
201179 nonce . Clear ( ) ;
@@ -212,7 +190,7 @@ public int Encrypt(ReadOnlySpan<byte> data, Span<byte> output)
212190 [ MethodImpl ( MethodImplOptions . AggressiveOptimization ) ]
213191 public int Decrypt ( ReadOnlySpan < byte > encryptedData , Span < byte > output )
214192 {
215- if ( _disableEncrypt )
193+ if ( disableEncrypt )
216194 {
217195 encryptedData . CopyTo ( output ) ;
218196 return encryptedData . Length ;
@@ -228,25 +206,25 @@ public int Decrypt(ReadOnlySpan<byte> encryptedData, Span<byte> output)
228206 using var aes = new AesGcm ( _key , TagSize ) ;
229207
230208 // OPTIMIZED: Use Span slicing (zero allocation)
231- var nonce = encryptedData . Slice ( 0 , NonceSize ) ;
209+ var nonce = encryptedData [ .. NonceSize ] ;
232210 var cipher = encryptedData . Slice ( NonceSize , cipherLength ) ;
233- var tag = encryptedData . Slice ( NonceSize + cipherLength , TagSize ) ;
211+ var tag = encryptedData [ ( NonceSize + cipherLength ) .. ] ;
234212
235213 // Decrypt directly to output
236- aes . Decrypt ( nonce , cipher , tag , output . Slice ( 0 , cipherLength ) ) ;
214+ aes . Decrypt ( nonce , cipher , tag , output [ .. cipherLength ] ) ;
237215
238216 return cipherLength ;
239217 }
240218
241219 /// <summary>
242220 /// Encrypts a page in-place using AES-256-GCM (zero-allocation).
243- /// Page format: [plaintext...] → [nonce(12)][ciphertext...][tag(16)]
221+ /// Page format: [plaintext...] → [nonce(12)][ciphertext...][tag(16)]
244222 /// </summary>
245223 /// <param name="page">The page buffer (must have space for nonce + tag overhead).</param>
246224 [ MethodImpl ( MethodImplOptions . AggressiveOptimization ) ]
247225 public void EncryptPage ( Span < byte > page )
248226 {
249- if ( _disableEncrypt )
227+ if ( disableEncrypt )
250228 return ;
251229
252230 var dataSize = page . Length - NonceSize - TagSize ;
@@ -269,23 +247,21 @@ public void EncryptPage(Span<byte> page)
269247 Span < byte > temp = tempArray . AsSpan ( 0 , dataSize ) ;
270248
271249 // Copy plaintext to temp
272- page . Slice ( 0 , dataSize ) . CopyTo ( temp ) ;
250+ page [ .. dataSize ] . CopyTo ( temp ) ;
273251
274- // Encrypt: temp → temp (in-place in temp buffer)
252+ // Encrypt: temp → temp (in-place in temp buffer)
275253 aes . Encrypt ( nonce , temp , temp , tag ) ;
276254
277255 // Write back: [nonce][ciphertext][tag]
278256 nonce . CopyTo ( page ) ;
279- temp . CopyTo ( page . Slice ( NonceSize ) ) ;
280- tag . CopyTo ( page . Slice ( NonceSize + dataSize ) ) ;
257+ temp . CopyTo ( page [ NonceSize .. ] ) ;
258+ tag . CopyTo ( page [ ( NonceSize + dataSize ) .. ] ) ;
281259 }
282260 finally
283261 {
284262 // SECURITY: Clear sensitive data
285263 if ( tempArray != null )
286- {
287264 _pool . Return ( tempArray , clearArray : true ) ;
288- }
289265
290266 nonce . Clear ( ) ;
291267 tag . Clear ( ) ;
@@ -294,13 +270,13 @@ public void EncryptPage(Span<byte> page)
294270
295271 /// <summary>
296272 /// Decrypts a page in-place using AES-256-GCM (zero-allocation).
297- /// Page format: [nonce(12)][ciphertext...][tag(16)] → [plaintext...]
273+ /// Page format: [nonce(12)][ciphertext...][tag(16)] → [plaintext...]
298274 /// </summary>
299275 /// <param name="page">The encrypted page buffer.</param>
300276 [ MethodImpl ( MethodImplOptions . AggressiveOptimization ) ]
301277 public void DecryptPage ( Span < byte > page )
302278 {
303- if ( _disableEncrypt )
279+ if ( disableEncrypt )
304280 return ;
305281
306282 var cipherLength = page . Length - NonceSize - TagSize ;
@@ -310,9 +286,9 @@ public void DecryptPage(Span<byte> page)
310286 using var aes = new AesGcm ( _key , TagSize ) ;
311287
312288 // OPTIMIZED: Extract components via Span slicing
313- var nonce = page . Slice ( 0 , NonceSize ) ;
289+ var nonce = page [ .. NonceSize ] ;
314290 var cipher = page . Slice ( NonceSize , cipherLength ) ;
315- var tag = page . Slice ( NonceSize + cipherLength , TagSize ) ;
291+ var tag = page [ ( NonceSize + cipherLength ) .. ] ;
316292
317293 byte [ ] ? tempArray = null ;
318294 try
@@ -331,9 +307,7 @@ public void DecryptPage(Span<byte> page)
331307 {
332308 // SECURITY: Clear sensitive data
333309 if ( tempArray != null )
334- {
335310 _pool . Return ( tempArray , clearArray : true ) ;
336- }
337311 }
338312 }
339313
@@ -342,9 +316,7 @@ public void DecryptPage(Span<byte> page)
342316 /// </summary>
343317 public void Dispose ( )
344318 {
345- if ( _key != null && _key . Length > 0 )
346- {
347- Array . Clear ( _key , 0 , _key . Length ) ;
348- }
319+ if ( _key . Length > 0 )
320+ Array . Clear ( _key ) ;
349321 }
350322}
0 commit comments