PostQuantum.FileEncryption.Hybrid
1.6.0
Requires NuGet 6.0.0 or higher.
dotnet add package PostQuantum.FileEncryption.Hybrid --version 1.6.0
NuGet\Install-Package PostQuantum.FileEncryption.Hybrid -Version 1.6.0
<PackageReference Include="PostQuantum.FileEncryption.Hybrid" Version="1.6.0" />
<PackageVersion Include="PostQuantum.FileEncryption.Hybrid" Version="1.6.0" />
<PackageReference Include="PostQuantum.FileEncryption.Hybrid" />
paket add PostQuantum.FileEncryption.Hybrid --version 1.6.0
#r "nuget: PostQuantum.FileEncryption.Hybrid, 1.6.0"
#:package PostQuantum.FileEncryption.Hybrid@1.6.0
#addin nuget:?package=PostQuantum.FileEncryption.Hybrid&version=1.6.0
#tool nuget:?package=PostQuantum.FileEncryption.Hybrid&version=1.6.0
PostQuantum.FileEncryption.Hybrid
The single recommended path for public-key file encryption in this suite. Encrypt files to a recipient's public key so that only their private key can open them — protected by X25519 + ML-KEM-768 together, so your data stays safe even if either primitive is later broken.
Fully managed (BouncyCastle) — no native ML-KEM / OpenSSL 3.5 requirement, so it runs anywhere
.NET 8 or later runs (net8.0 and net10.0 targets). Produces standard .pqfe containers.
dotnet add package PostQuantum.FileEncryption.Hybrid --version 1.6.0
Versioning. This package is intentionally kept in lockstep with
PostQuantum.FileEncryption: every release of one ships at the same version as the other, and Hybrid's pack pins the matching core version. The two packages are one cryptographic unit — the core owns the.pqfecontainer and the chunk/AEAD engine; Hybrid plugs into the core'sKeyEstablishmentseam to add the public-key path. A version mismatch would mean Hybrid is talking to a different format/engine than the one it was designed against. See docs/VERSIONING.md.
Migrating from the deprecated inline ML-KEM-only mode
If you previously used PqKeyPair / PqRecipientPublicKey / PqRecipientPrivateKey and the
recipient overloads on PqFileEncryptor / PqFileDecryptor in the core
PostQuantum.FileEncryption package, those are deprecated (PQFE002) as of 1.0.0-rc.2.
They remain for source-compatibility but emit a deprecation warning; removal is targeted for
a future major release.
// Before (deprecated PQFE001 + PQFE002 in the core package, platform-gated by ML-KEM):
using var keyPair = PqKeyPair.Generate();
await new PqFileEncryptor().EncryptFileAsync("plain.bin", "cipher.pqfe", keyPair.PublicKey);
await new PqFileDecryptor().DecryptFileAsync("cipher.pqfe", "out.bin", keyPair.PrivateKey);
// After (this package — hybrid combiner, runs everywhere, no platform gate):
using var keyPair = PqHybridKeyPair.Generate();
await new PqHybridEncryptor().EncryptFileAsync("plain.bin", "cipher.pqfe", keyPair.PublicKey);
await new PqHybridDecryptor().DecryptFileAsync("cipher.pqfe", "out.bin", keyPair.PrivateKey);
Usage
using PostQuantum.FileEncryption.Hybrid;
// Recipient: generate once, publish the public key, keep the private key safe.
using var keyPair = PqHybridKeyPair.Generate();
byte[] publish = keyPair.PublicKey.Export();
// Sender: encrypt to the public key.
var recipient = PqHybridPublicKey.Import(publish);
byte[] container = await new PqHybridEncryptor().EncryptBytesAsync(secretBytes, recipient);
// Recipient: decrypt with the private key.
byte[] plaintext = await new PqHybridDecryptor().DecryptBytesAsync(container, keyPair.PrivateKey);
Storing the private key
PrivateKey.Export() returns raw secret bytes; for storage, prefer the passphrase-encrypted
form — an authenticated, Argon2id-hardened key file that fails closed on a wrong passphrase
or any tampering:
byte[] keyFile = keyPair.PrivateKey.ExportEncrypted(passphrase); // store this
using var privateKey = PqHybridPrivateKey.ImportEncrypted(keyFile, passphrase);
See docs/KEY-FILE-FORMAT.md
for the byte-exact format (a standard .pqfe container behind a five-byte framing — no new
cryptography).
Multiple recipients
var recipients = new[] { alice, bob, carol }; // PqHybridPublicKey[]
await new PqHybridEncryptor().EncryptFileToAsync("report.pdf", "report.pqfe", recipients);
// Any one of alice/bob/carol can decrypt with their own private key.
Up to 55 recipients per container. The limit is not arbitrary: each recipient entry is
1,186 bytes (the ML-KEM-768 ciphertext dominates it), and the frozen .pqfe v2 header
carries all entries in a length field capped at 65,535 bytes — ⌊65,534 / 1,186⌋ = 55. The
limit is enforced before any wrapping work, and widening the field is banked as a
format-v3 candidate (KNOWN-GAPS.md).
For genuinely larger audiences, wrap to a group key held in a KMS via IContentKeyProvider
instead — access-group membership then lives where it can be revoked.
File and stream APIs (EncryptFileAsync, EncryptAsync, DecryptFileAsync, DecryptAsync) are
also available, with atomic file output and progress reporting.
How it works
X25519 ECDH and ML-KEM-768 encapsulation each produce a shared secret; HKDF-SHA256 combines them
(ss_pq ‖ ss_classical) into a key-wrapping key that AES-256-GCM uses to wrap a random content
key. See the format spec
(KeySource = 3 and 4),
docs/HYBRID-COMBINER.md
(design rationale — how this relates to X-Wing, HPKE, and RFC 9794 terminology), and
docs/ROADMAP-v3.md.
To God be the glory — 1 Corinthians 10:31.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- BouncyCastle.Cryptography (>= 2.6.2)
- PostQuantum.FileEncryption (>= 1.6.0)
-
net8.0
- BouncyCastle.Cryptography (>= 2.6.2)
- PostQuantum.FileEncryption (>= 1.6.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on PostQuantum.FileEncryption.Hybrid:
| Package | Downloads |
|---|---|
|
PostQuantum.FileEncryption.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection integration for PostQuantum.FileEncryption, for .NET 8 and .NET 10. Adds AddPqFileEncryption(), AddPqHybridFileEncryption(), and AddPqSigning() extension methods that register PqFileEncryptor/PqFileDecryptor, PqHybridEncryptor/PqHybridDecryptor, and PqSigner/PqVerifier as singletons, with optional PqEncryptionOptions. Brings the core library (constant-memory streaming AES-256-GCM over the FROZEN .pqfe v2 container, PBKDF2-HMAC-SHA256 or Argon2id), the production X25519 + ML-KEM-768 hybrid package, and detached Ed25519 + ML-DSA-65 signatures into any host using the standard .NET service container — ASP.NET Core, Worker Services, console hosts. Public API surface locked by Microsoft.CodeAnalysis.PublicApiAnalyzers; CycloneDX SBOM and SLSA-style build-provenance attestation on every release. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.6.0 | 303 | 7/11/2026 |
| 1.5.0 | 344 | 7/2/2026 |
| 1.4.1 | 913 | 6/13/2026 |
| 1.4.0 | 846 | 6/13/2026 |
| 1.3.0 | 1,079 | 6/13/2026 |
| 1.2.1 | 889 | 6/12/2026 |
| 1.2.0 | 899 | 6/12/2026 |
| 1.1.0 | 942 | 6/10/2026 |
| 1.0.1 | 1,058 | 6/6/2026 |
| 1.0.0 | 1,190 | 6/6/2026 |
| 1.0.0-rc.3 | 77 | 6/4/2026 |
| 1.0.0-rc.2 | 67 | 6/2/2026 |
| 1.0.0-rc.1 | 76 | 5/31/2026 |
| 0.2.0 | 1,137 | 5/31/2026 |
1.5.0 — tracks the family in lockstep. PqHybridPrivateKey gains ExportEncrypted/ImportEncrypted (passphrase-protected PQKF key files, Argon2id by default, optional PqDecryptionLimits on import) and IsEncryptedKeyFile. PqHybridDecryptor gains a PqDecryptionLimits constructor, closing the last documented denial-of-service exposure for containers from untrusted sources. Hybrid recipient decryption is now pinned by a byte-exact known-answer vector. No change to the .pqfe v2 container format, which remains FROZEN for the 1.x line. See CHANGELOG.md.