Scenario 7.3: File Encryption

Category: Encryption
Complexity: * (Medium)
Prerequisites: Key material or password
Estimated Time: 15-20 minutes </WRAP> —- ===== Description ===== This scenario describes secure file encryption with Post-Quantum secure methods. The implementation supports both password-based and key-based encryption. Use cases: * Backup encryption * Document protection * Configuration files * Archiving —- ===== Workflow ===== <mermaid> flowchart TD FILE[Original file] –> COMPRESS[Optional: Compress] COMPRESS –> CHUNK[Split into chunks] CHUNK –> ENCRYPT[AES-256-GCM per chunk] subgraph Key Derivation PWD[Password] –> KDF[Argon2id] KDF –> KEY[Encryption Key] end KEY –> ENCRYPT ENCRYPT –> OUTPUT[Encrypted file] style KEY fill:#e8f5e9 style OUTPUT fill:#e3f2fd </mermaid> —- ===== Code Example: Encrypt File ===== <code csharp> using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ; using System.Security.Cryptography; using var ctx = PqCryptoContext.Initialize(); Source file string inputFile = „document.pdf“; string outputFile = „document.pdf.enc“; string password = „MySecurePassword123!“; Encryption parameters var salt = RandomNumberGenerator.GetBytes(32); var nonce = RandomNumberGenerator.GetBytes(12); Derive key from password (Argon2id) var key = ctx.DeriveKeyArgon2id( password: Encoding.UTF8.GetBytes(password), salt: salt, outputLength: 32, iterations: 3, memoryKiB: 65536, 64 MB parallelism: 4 ); Read file var plaintext = File.ReadAllBytes(inputFile); Encrypt with AES-256-GCM var ciphertext = new byte[plaintext.Length]; var tag = new byte[16]; using var aes = new OpenSslAesGcm(key); aes.Encrypt(nonce, plaintext, ciphertext, tag); Write header + ciphertext using var output = File.Create(outputFile); using var writer = new BinaryWriter(output); Magic number writer.Write(Encoding.ASCII.GetBytes(„PQENC“)); Version writer.Write1)

1)
byte)1); Salt writer.Write(salt.Length); writer.Write(salt); Nonce writer.Write(nonce.Length); writer.Write(nonce); Tag writer.Write(tag.Length); writer.Write(tag); Ciphertext writer.Write(ciphertext.Length); writer.Write(ciphertext); Console.WriteLine($„File encrypted: {outputFile}“); Console.WriteLine($„ Original: {plaintext.Length:N0} bytes“); Console.WriteLine($„ Encrypted: {output.Length:N0} bytes“); </code>
===== Code Example: Decrypt File =====
using var ctx = PqCryptoContext.Initialize();
 
string encryptedFile = "document.pdf.enc";
string outputFile = "document-decrypted.pdf";
string password = "MySecurePassword123!";
 
using var input = File.OpenRead(encryptedFile);
using var reader = new BinaryReader(input);
 
// Check magic number
var magic = Encoding.ASCII.GetString(reader.ReadBytes(5));
if (magic != "PQENC")
    throw new InvalidDataException("Invalid file format");
 
// Check version
var version = reader.ReadByte();
if (version != 1)
    throw new NotSupportedException($"Version {version} not supported");
 
// Read parameters
var saltLen = reader.ReadInt32();
var salt = reader.ReadBytes(saltLen);
var nonceLen = reader.ReadInt32();
var nonce = reader.ReadBytes(nonceLen);
var tagLen = reader.ReadInt32();
var tag = reader.ReadBytes(tagLen);
var ciphertextLen = reader.ReadInt32();
var ciphertext = reader.ReadBytes(ciphertextLen);
 
// Derive key (same parameters!)
var key = ctx.DeriveKeyArgon2id(
    password: Encoding.UTF8.GetBytes(password),
    salt: salt,
    outputLength: 32,
    iterations: 3,
    memoryKiB: 65536,
    parallelism: 4
);
 
// Decrypt
var plaintext = new byte[ciphertext.Length];
 
using var aes = new OpenSslAesGcm(key);
aes.Decrypt(nonce, ciphertext, tag, plaintext);
 
// Save
File.WriteAllBytes(outputFile, plaintext);
 
Console.WriteLine($"File decrypted: {outputFile}");

===== Streaming for Large Files =====
public class FileEncryptor
{
    private const int ChunkSize = 64 * 1024;  // 64 KB chunks
 
    public void EncryptLargeFile(
        string inputPath,
        string outputPath,
        byte[] key)
    {
        using var ctx = PqCryptoContext.Initialize();
        using var input = File.OpenRead(inputPath);
        using var output = File.Create(outputPath);
        using var writer = new BinaryWriter(output);
 
        // Write header
        writer.Write(Encoding.ASCII.GetBytes("PQENC"));
        writer.Write((byte)2);  // Version 2 = Streaming
 
        // Number of chunks
        var totalChunks = (int)Math.Ceiling((double)input.Length / ChunkSize);
        writer.Write(totalChunks);
 
        var buffer = new byte[ChunkSize];
        var chunkIndex = 0;
 
        while (input.Position < input.Length)
        {
            var bytesRead = input.Read(buffer, 0, ChunkSize);
            var chunk = buffer.AsSpan(0, bytesRead).ToArray();
 
            // Unique nonce per chunk (ChunkIndex + Random)
            var nonce = new byte[12];
            BitConverter.GetBytes(chunkIndex).CopyTo(nonce, 0);
            RandomNumberGenerator.Fill(nonce.AsSpan(4));
 
            var ciphertext = new byte[bytesRead];
            var tag = new byte[16];
 
            using var aes = new OpenSslAesGcm(key);
            // AAD = Chunk index for order protection
            var aad = BitConverter.GetBytes(chunkIndex);
            aes.Encrypt(nonce, chunk, ciphertext, tag, aad);
 
            // Write chunk
            writer.Write(nonce);
            writer.Write(tag);
            writer.Write(ciphertext.Length);
            writer.Write(ciphertext);
 
            chunkIndex++;
        }
 
        Console.WriteLine($"Encrypted: {chunkIndex} chunks");
    }
 
    public void DecryptLargeFile(
        string inputPath,
        string outputPath,
        byte[] key)
    {
        using var input = File.OpenRead(inputPath);
        using var reader = new BinaryReader(input);
        using var output = File.Create(outputPath);
 
        // Read header
        var magic = Encoding.ASCII.GetString(reader.ReadBytes(5));
        if (magic != "PQENC") throw new InvalidDataException();
 
        var version = reader.ReadByte();
        if (version != 2) throw new NotSupportedException();
 
        var totalChunks = reader.ReadInt32();
 
        for (int i = 0; i < totalChunks; i++)
        {
            var nonce = reader.ReadBytes(12);
            var tag = reader.ReadBytes(16);
            var ciphertextLen = reader.ReadInt32();
            var ciphertext = reader.ReadBytes(ciphertextLen);
 
            var plaintext = new byte[ciphertextLen];
 
            using var aes = new OpenSslAesGcm(key);
            var aad = BitConverter.GetBytes(i);
            aes.Decrypt(nonce, ciphertext, tag, plaintext, aad);
 
            output.Write(plaintext);
        }
    }
}

===== Hybrid Encryption with ML-KEM =====
public class HybridFileEncryptor
{
    public void EncryptForRecipient(
        string inputPath,
        string outputPath,
        byte[] recipientMlKemPublicKey)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // ML-KEM key encapsulation
        var pubKey = ctx.ImportPublicKey(recipientMlKemPublicKey);
        var (kemCiphertext, sharedSecret) = ctx.Encapsulate(pubKey);
 
        // Derive file encryption key
        var fileKey = ctx.DeriveKey(
            sharedSecret,
            outputLength: 32,
            info: Encoding.UTF8.GetBytes("file-encryption-key")
        );
 
        // Encrypt file
        var encryptor = new FileEncryptor();
        var tempFile = Path.GetTempFileName();
        encryptor.EncryptLargeFile(inputPath, tempFile, fileKey);
 
        // Output with KEM ciphertext
        using var output = File.Create(outputPath);
        using var writer = new BinaryWriter(output);
 
        writer.Write(Encoding.ASCII.GetBytes("PQKEM"));
        writer.Write((byte)1);
        writer.Write(kemCiphertext.Length);
        writer.Write(kemCiphertext);
 
        // Append encrypted file
        using var encryptedContent = File.OpenRead(tempFile);
        encryptedContent.CopyTo(output);
 
        File.Delete(tempFile);
    }
}

===== File Format Specification =====
PQENC v1 (Single-Shot):
+---------------------------------+
| Magic: "PQENC" (5 bytes)        |
| Version: 0x01 (1 byte)          |
| Salt Length (4 bytes)           |
| Salt (variable)                 |
| Nonce Length (4 bytes)          |
| Nonce (12 bytes)                |
| Tag Length (4 bytes)            |
| Tag (16 bytes)                  |
| Ciphertext Length (4 bytes)     |
| Ciphertext (variable)           |
+---------------------------------+

PQENC v2 (Streaming):
+---------------------------------+
| Magic: "PQENC" (5 bytes)        |
| Version: 0x02 (1 byte)          |
| Total Chunks (4 bytes)          |
+---------------------------------+
| Chunk 0:                        |
|   Nonce (12 bytes)              |
|   Tag (16 bytes)                |
|   Ciphertext Length (4 bytes)   |
|   Ciphertext (variable)         |
+---------------------------------+
| Chunk 1: ...                    |
+---------------------------------+

===== Related Scenarios =====
Relationship Scenario Description
Component 7.2 Key Encapsulation ML-KEM for recipients
Related 7.1 Hybrid Encryption Hybrid concept
Related 4.4 Backup Backup encryption

« <- 7.2 Key Encapsulation | ^ Encryption Overview | -> All Scenarios »
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional
Zuletzt geändert: on 2026/01/30 at 12:35 AM

Powered by DokuWiki