====== SymmetricEncryptionExtensions ======
**Namespace:** ''WvdS.System.Security.Cryptography.Encryption''
Static class for AES-GCM encryption with Post-Quantum key support. Supports classical, hybrid and pure PQ encryption.
===== Overview =====
This class provides three encryption approaches:
| Mode | Classical | ML-KEM | Usage |
| Classic | RSA-OAEP / ECDH | - | Standard .NET behavior |
| Hybrid | RSA-OAEP / ECDH | ✓ | Maximum security |
| PostQuantum | - | ✓ | Pure post-quantum |
===== AES-GCM with PQ Key =====
==== EncryptWithPqKey ====
Encrypts data with AES-GCM using an ML-KEM shared secret.
// Shared Secret from ML-KEM Key Exchange
byte[] sharedSecret = session.SharedSecret;
// Encrypt
byte[] plaintext = Encoding.UTF8.GetBytes("Secret message");
byte[] encrypted = SymmetricEncryptionExtensions.EncryptWithPqKey(
plaintext,
sharedSecret);
// With Additional Authenticated Data (AAD)
byte[] aad = Encoding.UTF8.GetBytes("Context-Info");
byte[] encryptedWithAad = SymmetricEncryptionExtensions.EncryptWithPqKey(
plaintext,
sharedSecret,
associatedData: aad);
==== DecryptWithPqKey ====
byte[] decrypted = SymmetricEncryptionExtensions.DecryptWithPqKey(
encrypted,
sharedSecret,
associatedData: aad); // If used during Encrypt
string message = Encoding.UTF8.GetString(decrypted);
===== Hybrid Encryption (RSA + ML-KEM) =====
Combines RSA-OAEP key encapsulation with ML-KEM for quantum-safe hybrid encryption.
==== EncryptHybrid ====
using var rsa = RSA.Create(4096);
var (mlKemPublicKey, mlKemPrivateKey) = PqCrypto.GenerateKeyPair();
byte[] plaintext = GetSecretData();
// Hybrid encryption (RSA + ML-KEM)
HybridEncryptedData encrypted = SymmetricEncryptionExtensions.EncryptHybrid(
plaintext,
rsa, // Recipient's RSA Public Key
mlKemPublicKey, // Recipient's ML-KEM Public Key
CryptoMode.Hybrid);
// Serialize for transport
byte[] serialized = encrypted.ToBytes();
==== DecryptHybrid ====
// Deserialize
HybridEncryptedData encrypted = HybridEncryptedData.FromBytes(serialized);
// Decrypt
byte[] plaintext = SymmetricEncryptionExtensions.DecryptHybrid(
encrypted,
rsaPrivateKey, // RSA Private Key
mlKemPrivateKey); // ML-KEM Private Key
===== ECDH + ML-KEM Encryption =====
ECIES-style encryption with ephemeral ECDH and ML-KEM.
==== EncryptEcdhPq ====
using var recipientEcdh = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP384);
var (mlKemPublicKey, mlKemPrivateKey) = PqCrypto.GenerateKeyPair();
byte[] plaintext = GetSecretData();
// ECDH + ML-KEM hybrid encryption
HybridEncryptedData encrypted = SymmetricEncryptionExtensions.EncryptEcdhPq(
plaintext,
recipientEcdh, // Recipient's ECDH Public Key
mlKemPublicKey, // ML-KEM Public Key
CryptoMode.Hybrid);
// Ephemeral ECDH Public Key is contained in encrypted.EphemeralPublicKey
==== DecryptEcdhPq ====
byte[] plaintext = SymmetricEncryptionExtensions.DecryptEcdhPq(
encrypted,
recipientEcdhPrivateKey,
mlKemPrivateKey);
===== Core AES-GCM Operations =====
Direct AES-256-GCM encryption without key encapsulation.
==== EncryptAesGcm ====
byte[] key = RandomNumberGenerator.GetBytes(32); // 256-bit Key
byte[] plaintext = GetData();
// Standard AES-GCM
byte[] encrypted = SymmetricEncryptionExtensions.EncryptAesGcm(
plaintext,
key);
// With AAD
byte[] aad = Encoding.UTF8.GetBytes("message-context");
byte[] encryptedWithAad = SymmetricEncryptionExtensions.EncryptAesGcm(
plaintext,
key,
associatedData: aad);
**Output format:**
+------------------------------------------+
| [12 Bytes] Nonce (randomly generated) |
| [n Bytes] Ciphertext |
| [16 Bytes] GCM Authentication Tag |
+------------------------------------------+
==== DecryptAesGcm ====
byte[] plaintext = SymmetricEncryptionExtensions.DecryptAesGcm(
encrypted,
key,
associatedData: aad); // If used
===== Stream-based Encryption =====
For large files with chunk-based processing.
==== EncryptStream ====
byte[] key = RandomNumberGenerator.GetBytes(32);
using var inputStream = File.OpenRead("large-file.dat");
using var outputStream = File.Create("large-file.enc");
SymmetricEncryptionExtensions.EncryptStream(
inputStream,
outputStream,
key,
chunkSize: 64 * 1024); // 64 KB chunks (default)
**Chunk format:**
+---------------------------------------------+
| [12 Bytes] Base Nonce |
+---------------------------------------------+
| Chunk 0: |
| [4 Bytes] Chunk length |
| [n Bytes] Encrypted data |
| [16 Bytes] GCM Tag |
+---------------------------------------------+
| Chunk 1: (Nonce = Base + 1) |
| [4 Bytes] Chunk length |
| [n Bytes] Encrypted data |
| [16 Bytes] GCM Tag |
+---------------------------------------------+
| ... more chunks ... |
+---------------------------------------------+
| [4 Bytes] End marker (0x00000000) |
+---------------------------------------------+
==== DecryptStream ====
using var inputStream = File.OpenRead("large-file.enc");
using var outputStream = File.Create("large-file.decrypted");
SymmetricEncryptionExtensions.DecryptStream(
inputStream,
outputStream,
key);
===== Key Derivation =====
==== DeriveAesKey ====
Derives an AES-256 key from a shared secret.
byte[] sharedSecret = GetMlKemSharedSecret();
// Standard derivation
byte[] aesKey = SymmetricEncryptionExtensions.DeriveAesKey(sharedSecret);
// With salt and info
byte[] salt = RandomNumberGenerator.GetBytes(32);
byte[] info = Encoding.UTF8.GetBytes("MyApp-Encryption-Key");
byte[] aesKeyCustom = SymmetricEncryptionExtensions.DeriveAesKey(
sharedSecret,
salt: salt,
info: info);
**Internal implementation:** HKDF-SHA256 with ''info="WvdS-PQ-AES-Key"''
==== DeriveMultipleKeys ====
Derives multiple keys for different purposes.
byte[] sharedSecret = GetMlKemSharedSecret();
var (encryptionKey, macKey, iv) = SymmetricEncryptionExtensions.DeriveMultipleKeys(
sharedSecret,
salt: optionalSalt);
// encryptionKey: 32 bytes (AES-256)
// macKey: 32 bytes (HMAC)
// iv: 16 bytes (Initialization vector)
===== HybridEncryptedData Class =====
Container for hybrid-encrypted data with serialization.
==== Properties ====
^ Property ^ Type ^ Description ^
| ''Mode'' | CryptoMode | Encryption mode used |
| ''ClassicEncapsulatedKey'' | byte[]? | RSA-encrypted content key |
| ''EphemeralPublicKey'' | byte[]? | Ephemeral ECDH public key |
| ''PqCiphertext'' | byte[]? | ML-KEM ciphertext |
| ''EncryptedContent'' | byte[] | AES-GCM encrypted data |
==== Serialization ====
HybridEncryptedData encrypted = EncryptData();
// To byte array
byte[] serialized = encrypted.ToBytes();
// From byte array
HybridEncryptedData restored = HybridEncryptedData.FromBytes(serialized);
===== PqCrypto Convenience Class =====
Simplified API for pure PQ encryption.
// Generate key pair
var (publicKey, privateKey) = PqCrypto.GenerateKeyPair();
// Encrypt
byte[] plaintext = Encoding.UTF8.GetBytes("Secret message");
var (ciphertext, encryptedData) = PqCrypto.Encrypt(plaintext, publicKey);
// Decrypt
byte[] decrypted = PqCrypto.Decrypt(ciphertext, encryptedData, privateKey);
===== Methods Overview =====
==== SymmetricEncryptionExtensions ====
^ Method ^ Parameters ^ Return ^
| ''EncryptWithPqKey'' | byte[] plaintext, byte[] sharedSecret, byte[]? aad | byte[] |
| ''DecryptWithPqKey'' | byte[] ciphertext, byte[] sharedSecret, byte[]? aad | byte[] |
| ''EncryptHybrid'' | byte[] plaintext, RSA pubKey, byte[] pqPubKey, CryptoMode? | HybridEncryptedData |
| ''DecryptHybrid'' | HybridEncryptedData, RSA privKey, byte[] pqPrivKey | byte[] |
| ''EncryptEcdhPq'' | byte[] plaintext, ECDiffieHellman pubKey, byte[] pqPubKey, CryptoMode? | HybridEncryptedData |
| ''DecryptEcdhPq'' | HybridEncryptedData, ECDiffieHellman privKey, byte[] pqPrivKey | byte[] |
| ''EncryptAesGcm'' | byte[] plaintext, byte[] key, byte[]? aad | byte[] |
| ''DecryptAesGcm'' | byte[] ciphertext, byte[] key, byte[]? aad | byte[] |
| ''EncryptStream'' | Stream input, Stream output, byte[] key, int chunkSize | void |
| ''DecryptStream'' | Stream input, Stream output, byte[] key | void |
| ''DeriveAesKey'' | byte[] sharedSecret, byte[]? salt, byte[]? info | byte[] |
| ''DeriveMultipleKeys'' | byte[] sharedSecret, byte[]? salt | (byte[], byte[], byte[]) |
==== PqCrypto ====
^ Method ^ Parameters ^ Return ^
| ''GenerateKeyPair'' | - | (byte[] PublicKey, byte[] PrivateKey) |
| ''Encrypt'' | byte[] plaintext, byte[] recipientPublicKey | (byte[] Ciphertext, byte[] EncryptedData) |
| ''Decrypt'' | byte[] ciphertext, byte[] encryptedData, byte[] privateKey | byte[] |
===== Complete Example =====
using WvdS.System.Security.Cryptography;
using WvdS.System.Security.Cryptography.Encryption;
// 1. Generate keys (Recipient)
using var rsa = RSA.Create(4096);
var (mlKemPublicKey, mlKemPrivateKey) = PqCrypto.GenerateKeyPair();
// 2. Transmit public keys to sender
// (In practice: certificate with embedded PQ keys)
// --- Sender ---
// 3. Encrypt message
byte[] message = File.ReadAllBytes("document.pdf");
HybridEncryptedData encrypted = SymmetricEncryptionExtensions.EncryptHybrid(
message,
rsa, // Recipient's RSA Public Key
mlKemPublicKey, // Recipient's ML-KEM Public Key
CryptoMode.Hybrid);
// 4. Serialize and send
byte[] package = encrypted.ToBytes();
File.WriteAllBytes("document.encrypted", package);
// --- Recipient ---
// 5. Receive and deserialize
byte[] receivedPackage = File.ReadAllBytes("document.encrypted");
HybridEncryptedData receivedData = HybridEncryptedData.FromBytes(receivedPackage);
// 6. Decrypt
byte[] decrypted = SymmetricEncryptionExtensions.DecryptHybrid(
receivedData,
rsa, // Own RSA Private Key
mlKemPrivateKey); // Own ML-KEM Private Key
File.WriteAllBytes("document.decrypted.pdf", decrypted);
===== Security Notes =====
* AES-GCM nonces must NEVER be reused
* In hybrid mode, the key is derived from classical AND PQ secret
* ''DeriveAesKey'' without salt is deterministic - only for specific use cases
* Stream encryption uses incremental nonces per chunk
**Key combination in hybrid mode:**
Combined Key = HKDF-SHA256(
ikm = classicSecret || pqSecret,
info = "WvdS-Hybrid-Key"
)
Even if an attacker compromises the classical secret, the encryption remains protected by the PQ secret (and vice versa).
===== See Also =====
* [[.:start|Encryption Namespace]]
* [[.:hybridencrypteddata|HybridEncryptedData]]
* [[.:pqcrypto|PqCrypto]]
* [[..:keyexchange:start|KeyExchange Namespace]]
* [[..:keyderivation:start|KeyDerivation Namespace]]
* [[en:int:pqcrypt:konzepte:algorithmen:ml-kem|ML-KEM Algorithm]]
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//