Skip to content

Commit 6349b40

Browse files
author
MPCoreDeveloper
committed
refactor objects to generics
1 parent 1299cec commit 6349b40

File tree

5 files changed

+227
-330
lines changed

5 files changed

+227
-330
lines changed

SharpCoreDB/Services/AesGcmEncryption.cs

Lines changed: 28 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

SharpCoreDB/Services/CryptoService.cs

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace SharpCoreDB.Services;
1515
/// Zero-allocation implementation of ICryptoService using PBKDF2 for key derivation and AES-256-GCM for encryption.
1616
/// OPTIMIZATION: Uses stackalloc and Span<byte> to eliminate LINQ allocations in Encrypt/Decrypt.
1717
/// </summary>
18-
public class CryptoService : ICryptoService
18+
public sealed class CryptoService : ICryptoService
1919
{
2020
private const int NonceSize = 12; // AesGcm.NonceByteSizes.MaxSize
2121
private const int TagSize = 16; // AesGcm.TagByteSizes.MaxSize
@@ -66,26 +66,21 @@ public byte[] DeriveKey(string password, string salt)
6666
// SECURITY FIX: Derive key using PBKDF2 with 600,000 iterations (OWASP/NIST 2024 recommendation)
6767
// Previous value of 10,000 was dangerously low against GPU brute force attacks
6868
// See: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
69-
var key = Rfc2898DeriveBytes.Pbkdf2(
70-
passwordBytes.Slice(0, passwordLen),
71-
saltBytes.Slice(0, saltLen),
69+
return Rfc2898DeriveBytes.Pbkdf2(
70+
passwordBytes[..passwordLen],
71+
saltBytes[..saltLen],
7272
600000, // SECURITY: Increased from 10,000 to 600,000 iterations
7373
HashAlgorithmName.SHA256,
7474
32);
75-
76-
return key;
7775
}
7876
finally
7977
{
8078
// SECURITY: Clear sensitive password data
8179
if (passwordArray != null)
82-
{
8380
ArrayPool<byte>.Shared.Return(passwordArray, clearArray: true);
84-
}
81+
8582
if (saltArray != null)
86-
{
8783
ArrayPool<byte>.Shared.Return(saltArray, clearArray: true);
88-
}
8984
}
9085
}
9186

@@ -123,9 +118,7 @@ public byte[] Encrypt(byte[] key, byte[] data)
123118
{
124119
// SECURITY: Clear cipher data
125120
if (cipherArray != null)
126-
{
127121
ArrayPool<byte>.Shared.Return(cipherArray, clearArray: true);
128-
}
129122

130123
// SECURITY: Clear stack buffers
131124
nonce.Clear();
@@ -156,22 +149,15 @@ public byte[] Decrypt(byte[] key, byte[] encryptedData)
156149
}
157150

158151
/// <inheritdoc />
159-
public void EncryptPage(Span<byte> page)
160-
{
152+
public void EncryptPage(Span<byte> page) =>
161153
// Delegate to AesGcmEncryption for page operations
162154
throw new NotImplementedException("Use GetAesGcmEncryption() for page-level operations");
163-
}
164155

165156
/// <inheritdoc />
166-
public void DecryptPage(Span<byte> page)
167-
{
157+
public void DecryptPage(Span<byte> page) =>
168158
// Delegate to AesGcmEncryption for page operations
169159
throw new NotImplementedException("Use GetAesGcmEncryption() for page-level operations");
170-
}
171160

172161
/// <inheritdoc />
173-
public AesGcmEncryption GetAesGcmEncryption(byte[] key)
174-
{
175-
return new AesGcmEncryption(key, false);
176-
}
162+
public AesGcmEncryption GetAesGcmEncryption(byte[] key) => new(key, false);
177163
}

0 commit comments

Comments
 (0)