From 589242273916f8f82a5759d3af1854164e7992e7 Mon Sep 17 00:00:00 2001 From: nd Date: Sun, 21 Jan 2024 20:36:36 +0100 Subject: [PATCH] Dev (#27) * Build target is .NET 8 now * SHA3 hash/MAC algorithms are now not the default anymore and being used as .NET replacements only * Separated base classes for asymmetric algorithms in PQC and non-PQC implementations (which have different serialization helpers) * Asymmetric key data is PKCS#8 now * Using AES-256-GCM AEAD (128 bit MAC) as default crypto algorithm for PAKE now * Default key exchange algorithm is NTRU now + Added Shake128/256 hash algorithms as .NET replacements + Added Ed25519 and Ed448 asymmetric signature algorithms + Added `BouncyCastleAsymmetricNonPqcPrivate/PublicSignatureKeyBase2` to support a signer which requires a context constructor parameter + Added ECDH algorithm as replacement for the .NET variant from wan24-Crypto + Added ECDSA algorithm as replacement for the .NET variant from wan24-Crypto + Added `BcEllipticCurves` ECDH and ECDSA elliptic curve helper + Added X25519 and X448 asymmetric key exchange algorithms + Added own serialization logic for FrodoKEM and NTRU and enabled the algorithms to be available per default + Added `CryptoEnvironment.(UpdateDefaultOptionsAfter)RemoveUnsupportedAlgorithms` - Fixed all SHA3 algorithms are considered to be post-quantum-safe - Fixed wrong asymmetric PQC key data serialization - Fixed PQC key exchange derive key from encapsulated secret methods had to clone the provided information (they'll be cleared from Bouncy Castle) --- .github/workflows/dotnet.yml | 2 +- README.md | 58 +++++-- .../wan24-Crypto-BC Docs.csproj | 2 +- src/wan24-Crypto-BC Tests/A_Initialization.cs | 8 + src/wan24-Crypto-BC Tests/Asymmetric_Tests.cs | 11 +- .../Compatibility_Tests.cs | 78 +++++++++ src/wan24-Crypto-BC Tests/Encryption_Tests.cs | 10 +- src/wan24-Crypto-BC Tests/Hashing_Tests.cs | 14 +- src/wan24-Crypto-BC Tests/Mac_Tests.cs | 12 +- .../wan24-Crypto-BC Tests.csproj | 10 +- .../AsymmetricBcEcDiffieHellmanAlgorithm.cs | 76 ++++++++ .../AsymmetricBcEcDiffieHellmanPrivateKey.cs | 132 ++++++++++++++ .../AsymmetricBcEcDiffieHellmanPublicKey.cs | 56 ++++++ .../AsymmetricBcEcDsaAlgorithm.cs | 76 ++++++++ .../AsymmetricBcEcDsaPrivateKey.cs | 87 ++++++++++ .../AsymmetricBcEcDsaPublicKey.cs | 101 +++++++++++ .../AsymmetricDilithiumPrivateKey.cs | 25 +-- .../AsymmetricDilithiumPublicKey.cs | 2 +- .../AsymmetricEd25519Algorithm.cs | 94 ++++++++++ .../AsymmetricEd25519PrivateKey.cs | 67 +++++++ .../AsymmetricEd25519PublicKey.cs | 56 ++++++ .../AsymmetricEd448Algorithm.cs | 95 ++++++++++ .../AsymmetricEd448PrivateKey.cs | 89 ++++++++++ .../AsymmetricEd448PublicKey.cs | 56 ++++++ .../AsymmetricFalconPrivateKey.cs | 12 +- .../AsymmetricFalconPublicKey.cs | 2 +- .../AsymmetricFrodoKemPrivateKey.cs | 78 ++++++++- .../AsymmetricFrodoKemPublicKey.cs | 53 +++++- .../AsymmetricKyberPrivateKey.cs | 17 +- .../AsymmetricKyberPublicKey.cs | 2 +- .../AsymmetricNtruEncryptPrivateKey.cs | 75 +++++++- .../AsymmetricNtruEncryptPublicKey.cs | 52 +++++- .../AsymmetricSphincsPlusPrivateKey.cs | 19 +- .../AsymmetricSphincsPlusPublicKey.cs | 2 +- .../AsymmetricX25519Algorithm.cs | 93 ++++++++++ .../AsymmetricX25519PrivateKey.cs | 127 ++++++++++++++ .../AsymmetricX25519PublicKey.cs | 55 ++++++ .../AsymmetricX448Algorithm.cs | 95 ++++++++++ .../AsymmetricX448PrivateKey.cs | 148 ++++++++++++++++ .../AsymmetricX448PublicKey.cs | 55 ++++++ src/wan24-Crypto-BC/BcEllipticCurves.cs | 50 ++++++ src/wan24-Crypto-BC/Bootstrap.cs | 26 ++- src/wan24-Crypto-BC/BouncyCastle.cs | 76 ++++++-- .../BouncyCastleAeadCipherAlgorithmBase.cs | 2 +- .../BouncyCastleAsymmetricAlgorithmBase.cs | 10 +- ...ncyCastleAsymmetricNonPqcPrivateKeyBase.cs | 164 ++++++++++++++++++ ...AsymmetricNonPqcPrivateSignatureKeyBase.cs | 60 +++++++ ...symmetricNonPqcPrivateSignatureKeyBase2.cs | 61 +++++++ ...uncyCastleAsymmetricNonPqcPublicKeyBase.cs | 75 ++++++++ ...eAsymmetricNonPqcPublicSignatureKeyBase.cs | 81 +++++++++ ...AsymmetricNonPqcPublicSignatureKeyBase2.cs | 82 +++++++++ ...BouncyCastleAsymmetricPqcPrivateKeyBase.cs | 152 ++++++++++++++++ ...AsymmetricPqcPrivateKeyExchangeKeyBase.cs} | 21 +-- ...leAsymmetricPqcPrivateSignatureKeyBase.cs} | 16 +- .../BouncyCastleAsymmetricPqcPublicKeyBase.cs | 74 ++++++++ ...tleAsymmetricPqcPublicSignatureKeyBase.cs} | 14 +- .../BouncyCastleAsymmetricPrivateKeyBase.cs | 120 ++++--------- .../BouncyCastleAsymmetricPublicKeyBase.cs | 67 +------ .../BouncyCastleBlockCipherAlgorithmBase.cs | 8 +- ...BouncyCastleBufferedCipherAlgorithmBase.cs | 2 +- .../BouncyCastleCipherAlgorithmBase.cs | 8 +- .../BouncyCastleCryptoTransform.cs | 12 +- .../BouncyCastleHashAlgorithm.cs | 15 +- .../BouncyCastleHmacAlgorithm.cs | 14 +- src/wan24-Crypto-BC/BouncyCastleRngWrapper.cs | 16 +- .../BouncyCastleStreamCipherAlgorithmBase.cs | 2 +- src/wan24-Crypto-BC/ChaCha20Rng.cs | 18 +- src/wan24-Crypto-BC/DisposableRngWrapper.cs | 14 +- .../EncryptionSerpent256CbcAlgorithm.cs | 2 +- .../EncryptionTwofish256CbcAlgorithm.cs | 2 +- ...lgorithm.cs => HashBcSha3_256Algorithm.cs} | 8 +- ...lgorithm.cs => HashBcSha3_384Algorithm.cs} | 6 +- ...lgorithm.cs => HashBcSha3_512Algorithm.cs} | 6 +- .../HashBcShake128Algorithm.cs | 45 +++++ .../HashBcShake256Algorithm.cs | 45 +++++ .../InternalReflectionExtensions.cs | 12 +- ...rithm.cs => MacBcHmacSha3_256Algorithm.cs} | 13 +- ...rithm.cs => MacBcHmacSha3_384Algorithm.cs} | 11 +- ...rithm.cs => MacBcHmacSha3_512Algorithm.cs} | 15 +- src/wan24-Crypto-BC/README.md | 58 +++++-- src/wan24-Crypto-BC/StreamCipherRng.cs | 3 +- src/wan24-Crypto-BC/XSalsa20Rng.cs | 18 +- src/wan24-Crypto-BC/wan24-Crypto-BC.csproj | 8 +- 83 files changed, 3201 insertions(+), 413 deletions(-) create mode 100644 src/wan24-Crypto-BC Tests/Compatibility_Tests.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanAlgorithm.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPrivateKey.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPublicKey.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricBcEcDsaAlgorithm.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricBcEcDsaPrivateKey.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricBcEcDsaPublicKey.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricEd25519Algorithm.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricEd25519PrivateKey.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricEd25519PublicKey.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricEd448Algorithm.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricEd448PrivateKey.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricEd448PublicKey.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricX25519Algorithm.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricX25519PrivateKey.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricX25519PublicKey.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricX448Algorithm.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricX448PrivateKey.cs create mode 100644 src/wan24-Crypto-BC/AsymmetricX448PublicKey.cs create mode 100644 src/wan24-Crypto-BC/BcEllipticCurves.cs create mode 100644 src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateKeyBase.cs create mode 100644 src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase.cs create mode 100644 src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2.cs create mode 100644 src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicKeyBase.cs create mode 100644 src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase.cs create mode 100644 src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase2.cs create mode 100644 src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyBase.cs rename src/wan24-Crypto-BC/{BouncyCastleAsymmetricPrivateKeyExchangeKeyBase.cs => BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase.cs} (76%) rename src/wan24-Crypto-BC/{BouncyCastleAsymmetricPrivateSignatureKeyBase.cs => BouncyCastleAsymmetricPqcPrivateSignatureKeyBase.cs} (63%) create mode 100644 src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPublicKeyBase.cs rename src/wan24-Crypto-BC/{BouncyCastleAsymmetricPublicSignatureKeyBase.cs => BouncyCastleAsymmetricPqcPublicSignatureKeyBase.cs} (75%) rename src/wan24-Crypto-BC/{HashSha3_256Algorithm.cs => HashBcSha3_256Algorithm.cs} (74%) rename src/wan24-Crypto-BC/{HashSha3_384Algorithm.cs => HashBcSha3_384Algorithm.cs} (78%) rename src/wan24-Crypto-BC/{HashSha3_512Algorithm.cs => HashBcSha3_512Algorithm.cs} (78%) create mode 100644 src/wan24-Crypto-BC/HashBcShake128Algorithm.cs create mode 100644 src/wan24-Crypto-BC/HashBcShake256Algorithm.cs rename src/wan24-Crypto-BC/{MacHmacSha3_256Algorithm.cs => MacBcHmacSha3_256Algorithm.cs} (71%) rename src/wan24-Crypto-BC/{MacHmacSha3_384Algorithm.cs => MacBcHmacSha3_384Algorithm.cs} (74%) rename src/wan24-Crypto-BC/{MacHmacSha3_512Algorithm.cs => MacBcHmacSha3_512Algorithm.cs} (69%) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 7db90ba..4890371 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x + dotnet-version: 8.0.x - name: Restore dependencies run: dotnet restore ./src/wan24-Crypto-BC.sln --ignore-failed-sources - name: Build lib diff --git a/README.md b/README.md index a3e1cbf..2246810 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ the `wan24-Crypto` library with these algorithms: | SPHINCS+ | 5 | SPHINCSPLUS | | FrodoKEM* | 6 | FRODOKEM | | NTRUEncrypt* | 7 | NTRUENCRYPT | +| Ed25519 | 8 | ED25519 | +| Ed448 | 9 | ED448 | +| X25519 | 10 | X25519 | +| X448 | 11 | X448 | | **Symmetric** | | | | ChaCha20 | 1 | CHACHA20 | | XSalsa20 | 2 | XSALSA20 | @@ -22,14 +26,6 @@ the `wan24-Crypto` library with these algorithms: | Serpent 256 GCM AEAD (128 bit MAC) | 6 | SERPENT256GCM | | Twofish 256 CBC (ISO10126 padding) | 7 | TWOFISH256CBC | | Twofish 256 GCM AEAD (128 bit MAC) | 8 | TWOFISH256GCM | -| **Hashing** | | | -| SHA3-256 | 5 | SHA3-256 | -| SHA3-384 | 6 | SHA3-384 | -| SHA3-512 | 7 | SHA3-512 | -| **MAC** | | | -| HMAC-SHA3-256 | 4 | HMAC-SHA3-256 | -| HMAC-SHA3-384 | 5 | HMAC-SHA3-384 | -| HMAC-SHA3-512 | 6 | HMAC-SHA3-512 | **NOTE**: FrodoKEM and NTRUEncrypt are currently disabled, 'cause there seems to be a bug (missing code) in the Bouncy Castle library for @@ -64,12 +60,29 @@ BouncyCastle.SetDefaults(); Per default the current `wan24-Crypto` default will be set as counter algorithms to `HybridAlgorithmHelper`. +Current Bouncy Castle default algorithms are: + +- Key exchange: NTRUEncrypt +- Signature: CRYSTALS-Dilithium +- Encryption: Serpent 256 bit CBC +- PAKE encryption: Serpent 256 bit GCM + Some algorithms of the `wan24-Crypto` library are not available on some platforms, that's why they need to be replaced in order to be used: | `wan24-Crypto` | `wan24-Crypto-BC` | | -------------- | ----------------- | +| `AsymmetricEcDiffieHellmanAlgorithm` | `AsymmetricBcEcDiffieHellmanAlgorithm` | +| `AsymmetricEcDsaAlgorithm` | `AsymmetricBcEcDsaAlgorithm` | | `EncryptionAes256CbcAlgorithm` | `EncryptionBcAes256CbcAlgorithm` | +| `HashShake128Algorithm` | `HashBcShake128Algorithm` | +| `HashShake256Algorithm` | `HashBcShake256Algorithm` | +| `HashSha3_256Algorithm` | `HashBcSha3_256Algorithm` | +| `HashSha3_384Algorithm` | `HashBcSha3_384Algorithm` | +| `HashSha3_512Algorithm` | `HashBcSha3_512Algorithm` | +| `MacHmacSha3_256Algorithm` | `MacBcHmacSha3_256Algorithm` | +| `MacHmacSha3_384Algorithm` | `MacBcHmacSha3_384Algorithm` | +| `MacHmacSha3_512Algorithm` | `MacBcHmacSha3_512Algorithm` | To replace all of them: @@ -77,15 +90,20 @@ To replace all of them: BouncyCastle.ReplaceNetAlgorithms(); ``` +**NOTE**: The Shake128/256 replacements don't support variable output length +and use the default output length of the `wan24-Crypto` implementations +instead. + ## Post quantum safety -These algorithms are designed for post quantum cryptography: +These asymmetric algorithms are designed for post quantum cryptography: - CRYSTALS-Kyber (key exchange) - CRYSTALS-Dilithium (signature) - FALCON (signature) - SPHINCS+ (signature) - FrodoKEM (key exchange) +- NTRUEncrypt (key exchange) Normally you want to use them in hybrid mode and use classical algorithms of the `wan24-Crypto` package as counter algorithm. To do this per default: @@ -98,12 +116,16 @@ CryptoHelper.ForcePostQuantumSafety(); This will use these algorithms as (counter) algorithms for asymmetric cryptography, in case you didn't define other post quantum algorithms already: -- CRYSTALS-Kyber (key exchange) +- NTRUEncrypt (key exchange) - CRYSTALS-Dilithium (signature) For using other algorithms instead: ```cs +// CRYSTALS-Kyber +HybridAlgorithmHelper.SignatureAlgorithm = + AsymmetricHelper.GetAlgorithm(AsymmetricKyberAlgorithm.ALGORITHM_NAME); + // FALCON HybridAlgorithmHelper.SignatureAlgorithm = AsymmetricHelper.GetAlgorithm(AsymmetricFalconAlgorithm.ALGORITHM_NAME); @@ -123,7 +145,7 @@ encryption: ```cs // Create options having a counter private key CryptoOptions options = EncryptionHelper.GetDefaultOptions(); -options.SetCounterPrivateKey(yourKyberPrivateKey); +options.SetCounterPrivateKey(yourNtruPrivateKey); // Encrypt using the options and your normal private key byte[] cipherData = rawData.Encrypt(yourNormalPrivateKey, options); @@ -209,3 +231,17 @@ used, if `/dev/random` was preferred. To disable `/dev/random`, set **NOTE**: Currently only stream ciphers are supported, because the cipher RNG implementation doesn't buffer pre-generated random data. + +## X/Ed448-Goldilocks and X/Ed25519 + +Just a short note on Curve448: Private and public keys have a different key +size: The private key has 456 bit, while the public key has 448 bit. Both key +sizes are supported for key generation and result in the same key sizes for +the private (456 bit) and the public (448 bit) key. The private key of a key +pair will always identify with 456 bit, while the public key will always +identify with 448 bit - no matter which key size was chosen for key pair +generation. + +The Ed448 signature is context based, but currently only an empty byte array +is being used as context data. Instead of a context you should use the purpose +free text, which can be given to the signature methods of `wan24-Crypto`. diff --git a/src/wan24-Crypto-BC Docs/wan24-Crypto-BC Docs.csproj b/src/wan24-Crypto-BC Docs/wan24-Crypto-BC Docs.csproj index 9d1300b..3bb59fd 100644 --- a/src/wan24-Crypto-BC Docs/wan24-Crypto-BC Docs.csproj +++ b/src/wan24-Crypto-BC Docs/wan24-Crypto-BC Docs.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 wan24_Crypto_BC_Docs enable enable diff --git a/src/wan24-Crypto-BC Tests/A_Initialization.cs b/src/wan24-Crypto-BC Tests/A_Initialization.cs index b1750a6..de0e118 100644 --- a/src/wan24-Crypto-BC Tests/A_Initialization.cs +++ b/src/wan24-Crypto-BC Tests/A_Initialization.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using wan24.Core; using wan24.Crypto.BC; +using wan24.Crypto.Tests; using wan24.ObjectValidation; namespace wan24_Crypto_BC_Tests @@ -15,6 +16,13 @@ public static void Init(TestContext tc) ValidateObject.Logger = (message) => Logging.WriteDebug(message); TypeHelper.Instance.AddAssemblies(typeof(wan24.Crypto.BC.Bootstrap).Assembly); wan24.Core.Bootstrap.Async().Wait(); + DisposableBase.CreateStackInfo = true; + DisposableRecordBase.CreateStackInfo = true; + ErrorHandling.ErrorHandler = (info) => + { + if (info.Exception is StackInfoException six) Logging.WriteError(six.StackInfo.Stack); + }; + SharedTests.Initialize(); BouncyCastle.ReplaceNetAlgorithms(); Logging.WriteDebug("wan24-Crypto-BC Tests initialized"); } diff --git a/src/wan24-Crypto-BC Tests/Asymmetric_Tests.cs b/src/wan24-Crypto-BC Tests/Asymmetric_Tests.cs index fa1f0e7..0b59d30 100644 --- a/src/wan24-Crypto-BC Tests/Asymmetric_Tests.cs +++ b/src/wan24-Crypto-BC Tests/Asymmetric_Tests.cs @@ -1,4 +1,6 @@ -using wan24.Crypto.Tests; +using wan24.Crypto; +using wan24.Crypto.BC; +using wan24.Crypto.Tests; namespace wan24_Crypto_Tests { @@ -6,6 +8,11 @@ namespace wan24_Crypto_Tests public class Asymmetric_Tests { [TestMethod] - public void AllAlgo_Tests() => AsymmetricTests.TestAllAlgorithms(); + public void AllAlgo_Tests() + { + Assert.IsTrue(AsymmetricHelper.Algorithms[AsymmetricEcDiffieHellmanAlgorithm.ALGORITHM_NAME] is AsymmetricBcEcDiffieHellmanAlgorithm); + Assert.IsTrue(AsymmetricHelper.Algorithms[AsymmetricEcDsaAlgorithm.ALGORITHM_NAME] is AsymmetricBcEcDsaAlgorithm); + AsymmetricTests.TestAllAlgorithms(); + } } } diff --git a/src/wan24-Crypto-BC Tests/Compatibility_Tests.cs b/src/wan24-Crypto-BC Tests/Compatibility_Tests.cs new file mode 100644 index 0000000..e38b4bb --- /dev/null +++ b/src/wan24-Crypto-BC Tests/Compatibility_Tests.cs @@ -0,0 +1,78 @@ +using System.Security.Cryptography; +using wan24.Core; +using wan24.Crypto; +using wan24.Crypto.BC; + +namespace wan24_Crypto_Tests +{ + [TestClass] + public class Compatibility_Tests + { + private static readonly byte[] TestData = [1, 2, 3]; + + [TestMethod] + public void EcDh_Tests() + { + using AsymmetricEcDiffieHellmanPrivateKey keyA = AsymmetricEcDiffieHellmanAlgorithm.Instance.CreateKeyPair(); + using AsymmetricBcEcDiffieHellmanPublicKey pubKeyA = new(keyA.PublicKey.KeyData.Array.CloneArray()); + using AsymmetricBcEcDiffieHellmanPrivateKey keyB = AsymmetricBcEcDiffieHellmanAlgorithm.Instance.CreateKeyPair(); + (byte[] secretB, byte[] kexB) = keyB.GetKeyExchangeData(pubKeyA); + byte[] secretA = keyA.DeriveKey(kexB); + Assert.IsTrue(secretA.SequenceEqual(secretB)); + } + + [TestMethod] + public void EcDsa_Tests() + { + using AsymmetricEcDsaPrivateKey netKey = AsymmetricEcDsaAlgorithm.Instance.CreateKeyPair(); + using AsymmetricBcEcDsaPrivateKey bcKey = new(netKey.KeyData.Array); + SignatureContainer signature = netKey.SignData(TestData); + Assert.IsTrue(bcKey.PublicKey.ValidateSignature(signature, TestData, throwOnError: false), ".NET signature vlidation with Bouncy Castle failed"); + signature = bcKey.SignData(TestData); + Assert.IsTrue(netKey.PublicKey.ValidateSignature(signature, TestData, throwOnError: false), "Bouncy Castle signature vlidation with .NET failed"); + } + + [TestMethod] + public void Aes256Cbc_Tests() + { + CryptoOptions options = new() + { + LeaveOpen = true + }; + using MemoryStream raw = new(TestData); + using MemoryStream cipher = new(); + using MemoryStream decrypted = new(); + EncryptionAes256CbcAlgorithm.Instance.Encrypt(raw, cipher, TestData, options); + cipher.Position = 0; + EncryptionAes256CbcAlgorithm.Instance.Decrypt(cipher, decrypted, TestData, options); + Assert.IsTrue(decrypted.ToArray().SequenceEqual(TestData)); + } + + [TestMethod] + public void Sha3_Tests() + { + if (!Shake128.IsSupported) return; + byte[] a, b; + foreach (HashAlgorithmBase[] algos in new HashAlgorithmBase[][]{ + [HashSha3_256Algorithm.Instance, HashBcSha3_256Algorithm.Instance], + [HashSha3_384Algorithm.Instance, HashBcSha3_384Algorithm.Instance], + [HashSha3_512Algorithm.Instance, HashBcSha3_512Algorithm.Instance], + }) + { + a = algos[0].Hash(TestData); + b = algos[1].Hash(TestData); + Assert.IsTrue(a.SequenceEqual(b), $"{algos[0].GetType()} ({a.Length}/{b.Length})"); + } + foreach (MacAlgorithmBase[] algos in new MacAlgorithmBase[][]{ + [MacHmacSha3_256Algorithm.Instance, MacBcHmacSha3_256Algorithm.Instance], + [MacHmacSha3_384Algorithm.Instance, MacBcHmacSha3_384Algorithm.Instance], + [MacHmacSha3_512Algorithm.Instance, MacBcHmacSha3_512Algorithm.Instance], + }) + { + a = algos[0].Mac(TestData, TestData); + b = algos[1].Mac(TestData, TestData); + Assert.IsTrue(a.SequenceEqual(b), $"{algos[0].GetType()} ({a.Length}/{b.Length})"); + } + } + } +} diff --git a/src/wan24-Crypto-BC Tests/Encryption_Tests.cs b/src/wan24-Crypto-BC Tests/Encryption_Tests.cs index 207d5e0..dcc1bc0 100644 --- a/src/wan24-Crypto-BC Tests/Encryption_Tests.cs +++ b/src/wan24-Crypto-BC Tests/Encryption_Tests.cs @@ -1,4 +1,6 @@ -using wan24.Crypto.Tests; +using wan24.Crypto; +using wan24.Crypto.BC; +using wan24.Crypto.Tests; namespace wan24_Crypto_Tests { @@ -6,6 +8,10 @@ namespace wan24_Crypto_Tests public class Encryption_Tests { [TestMethod] - public async Task All_Tests() => await EncryptionTests.TestAllAlgorithms(); + public async Task All_Tests() + { + Assert.IsTrue(EncryptionHelper.Algorithms[EncryptionAes256CbcAlgorithm.ALGORITHM_NAME] is EncryptionBcAes256CbcAlgorithm); + await EncryptionTests.TestAllAlgorithms(); + } } } diff --git a/src/wan24-Crypto-BC Tests/Hashing_Tests.cs b/src/wan24-Crypto-BC Tests/Hashing_Tests.cs index aff8cfb..82a58ff 100644 --- a/src/wan24-Crypto-BC Tests/Hashing_Tests.cs +++ b/src/wan24-Crypto-BC Tests/Hashing_Tests.cs @@ -1,4 +1,6 @@ -using wan24.Crypto.Tests; +using wan24.Crypto; +using wan24.Crypto.BC; +using wan24.Crypto.Tests; namespace wan24_Crypto_Tests { @@ -6,6 +8,14 @@ namespace wan24_Crypto_Tests public class Hashing_Tests { [TestMethod] - public async Task All_Tests() => await HashingTests.TestAllAlgorithms(); + public async Task All_Tests() + { + Assert.IsTrue(HashHelper.Algorithms[HashSha3_256Algorithm.ALGORITHM_NAME] is HashBcSha3_256Algorithm); + Assert.IsTrue(HashHelper.Algorithms[HashSha3_384Algorithm.ALGORITHM_NAME] is HashBcSha3_384Algorithm); + Assert.IsTrue(HashHelper.Algorithms[HashSha3_512Algorithm.ALGORITHM_NAME] is HashBcSha3_512Algorithm); + Assert.IsTrue(HashHelper.Algorithms[HashShake128Algorithm.ALGORITHM_NAME] is HashBcShake128Algorithm); + Assert.IsTrue(HashHelper.Algorithms[HashShake256Algorithm.ALGORITHM_NAME] is HashBcShake256Algorithm); + await HashingTests.TestAllAlgorithms(); + } } } diff --git a/src/wan24-Crypto-BC Tests/Mac_Tests.cs b/src/wan24-Crypto-BC Tests/Mac_Tests.cs index 301f8a7..04b23f7 100644 --- a/src/wan24-Crypto-BC Tests/Mac_Tests.cs +++ b/src/wan24-Crypto-BC Tests/Mac_Tests.cs @@ -1,4 +1,6 @@ -using wan24.Crypto.Tests; +using wan24.Crypto; +using wan24.Crypto.BC; +using wan24.Crypto.Tests; namespace wan24_Crypto_Tests { @@ -6,6 +8,12 @@ namespace wan24_Crypto_Tests public class Mac_Tests { [TestMethod] - public async Task All_Tests() => await MacTests.TestAllAlgorithms(); + public async Task All_Tests() + { + Assert.IsTrue(MacHelper.Algorithms[MacHmacSha3_256Algorithm.ALGORITHM_NAME] is MacBcHmacSha3_256Algorithm); + Assert.IsTrue(MacHelper.Algorithms[MacHmacSha3_384Algorithm.ALGORITHM_NAME] is MacBcHmacSha3_384Algorithm); + Assert.IsTrue(MacHelper.Algorithms[MacHmacSha3_512Algorithm.ALGORITHM_NAME] is MacBcHmacSha3_512Algorithm); + await MacTests.TestAllAlgorithms(); + } } } diff --git a/src/wan24-Crypto-BC Tests/wan24-Crypto-BC Tests.csproj b/src/wan24-Crypto-BC Tests/wan24-Crypto-BC Tests.csproj index 824c567..c19bd05 100644 --- a/src/wan24-Crypto-BC Tests/wan24-Crypto-BC Tests.csproj +++ b/src/wan24-Crypto-BC Tests/wan24-Crypto-BC Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 wan24_Crypto_BC_Tests2 enable enable @@ -12,16 +12,16 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanAlgorithm.cs b/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanAlgorithm.cs new file mode 100644 index 0000000..248c337 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanAlgorithm.cs @@ -0,0 +1,76 @@ +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using System.Collections.ObjectModel; + +namespace wan24.Crypto.BC +{ + /// + /// EC Diffie Hellman asymmetric algorithm (may be used as replacement, if the .NET algorithm isn't available on the current platform) + /// + public sealed record class AsymmetricBcEcDiffieHellmanAlgorithm + : BouncyCastleAsymmetricAlgorithmBase< + AsymmetricBcEcDiffieHellmanPublicKey, + AsymmetricBcEcDiffieHellmanPrivateKey, + ECKeyPairGenerator, + ECKeyGenerationParameters, + ECDomainParameters, + ECPublicKeyParameters, + ECPrivateKeyParameters, + AsymmetricBcEcDiffieHellmanAlgorithm + > + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "ECDH"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 0; + /// + /// Default key size in bits + /// + public const int DEFAULT_KEY_SIZE = 521; + /// + /// Algorithm usages + /// + public const AsymmetricAlgorithmUsages USAGES = AsymmetricAlgorithmUsages.KeyExchange; + /// + /// Display name + /// + public const string DISPLAY_NAME = "EC Diffie Hellman"; + + /// + /// Allowed key sizes in bits + /// + private static readonly ReadOnlyCollection _AllowedKeySizes; + + /// + /// Static constructor + /// + static AsymmetricBcEcDiffieHellmanAlgorithm() => _AllowedKeySizes = new List() + { + 256, + 384, + 521 + }.AsReadOnly(); + + /// + /// Constructor + /// + public AsymmetricBcEcDiffieHellmanAlgorithm() + : base(ALGORITHM_NAME, ALGORITHM_VALUE, USAGES, isEllipticCurveAlgorithm: true, _AllowedKeySizes, isPostQuantum: false, DEFAULT_KEY_SIZE) + { } + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + protected override ECKeyGenerationParameters CreateKeyGenParameters(SecureRandom random, ECDomainParameters parameters, CryptoOptions options) + => new(parameters, random); + + /// + protected override ECDomainParameters GetEngineParameters(CryptoOptions options) => BcEllipticCurves.GetCurve(options.AsymmetricKeyBits); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPrivateKey.cs new file mode 100644 index 0000000..34023a1 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPrivateKey.cs @@ -0,0 +1,132 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// EC Diffie Hellman asymmetric private key + /// + public sealed record class AsymmetricBcEcDiffieHellmanPrivateKey + : BouncyCastleAsymmetricNonPqcPrivateKeyBase< + AsymmetricBcEcDiffieHellmanPublicKey, + AsymmetricBcEcDiffieHellmanAlgorithm, + ECPublicKeyParameters, + ECPrivateKeyParameters, + AsymmetricBcEcDiffieHellmanPrivateKey + >, IKeyExchangePrivateKey + { + /// + /// Constructor + /// + public AsymmetricBcEcDiffieHellmanPrivateKey() : base(AsymmetricBcEcDiffieHellmanAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricBcEcDiffieHellmanPrivateKey(byte[] keyData) : base(AsymmetricBcEcDiffieHellmanAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Keys + public AsymmetricBcEcDiffieHellmanPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricBcEcDiffieHellmanAlgorithm.ALGORITHM_NAME, keys) { } + + /// + public override (byte[] Key, byte[] KeyExchangeData) GetKeyExchangeData(IAsymmetricPublicKey? publicKey = null, CryptoOptions? options = null) + { + try + { + EnsureUndisposed(); + if (CryptoHelper.StrictPostQuantumSafety) throw new InvalidOperationException($"Post quantum safety-forced - {Algorithm.Name} isn't post quantum"); + publicKey ??= options?.PublicKey ?? options?.PrivateKey?.PublicKey ?? PublicKey; + if (publicKey is not AsymmetricBcEcDiffieHellmanPublicKey key) throw new ArgumentException($"Public {Algorithm.Name} key required", nameof(publicKey)); + return (DeriveKey(publicKey), PublicKey.KeyData.Array.CloneArray()); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + public override byte[] DeriveKey(byte[] keyExchangeData) + { + try + { + EnsureUndisposed(); + if (CryptoHelper.StrictPostQuantumSafety) throw new InvalidOperationException($"Post quantum safety-forced - {Algorithm.Name} isn't post quantum"); + using AsymmetricBcEcDiffieHellmanPublicKey publicKey = new(keyExchangeData); + return DeriveKey(publicKey as IAsymmetricPublicKey); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + public override byte[] DeriveKey(IAsymmetricPublicKey publicKey) + { + try + { + EnsureUndisposed(); + if (CryptoHelper.StrictPostQuantumSafety) throw new InvalidOperationException($"Post quantum safety-forced - {Algorithm.Name} isn't post quantum"); + if (publicKey is not AsymmetricBcEcDiffieHellmanPublicKey key) throw new ArgumentException($"Public {Algorithm.Name} key required", nameof(publicKey)); + ECDHBasicAgreement agreement = new(); + agreement.Init(PrivateKey); + BigInteger secret = agreement.CalculateAgreement(key.PublicKey); + try + { + using SecureByteArrayRefStruct sharedSecret = new(secret.ToByteArray()); + using SecureByteArrayRefStruct normalizedSecret = new(len: (Bits + 7) / 8); + sharedSecret.Span[Math.Max(0, sharedSecret.Length - normalizedSecret.Length)..] + .CopyTo(normalizedSecret.Span.Slice(Math.Max(0, normalizedSecret.Length - sharedSecret.Length), Math.Min(sharedSecret.Length, normalizedSecret.Length))); + return HashHelper.GetAlgorithm(HashSha256Algorithm.ALGORITHM_NAME).Hash(normalizedSecret.Array); + } + finally + { + secret.ClearPrivateByteArrayFields(); + } + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override ECPublicKeyParameters GetPublicKey(ECPrivateKeyParameters privateKey) => new(privateKey.Parameters.G.Multiply(privateKey.D), privateKey.Parameters); + + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (Keys?.Private is not ECPrivateKeyParameters privateKey) return; + privateKey.D.ClearPrivateByteArrayFields();//TODO All fields are private :( + } + + /// + protected override async Task DisposeCore() + { + await base.DisposeCore().DynamicContext(); + if (Keys?.Private is not ECPrivateKeyParameters privateKey) return; + privateKey.D.ClearPrivateByteArrayFields();//TODO All fields are private :( + } + + /// + /// Cast to public key + /// + /// Private key + public static implicit operator AsymmetricBcEcDiffieHellmanPublicKey(AsymmetricBcEcDiffieHellmanPrivateKey privateKey) => privateKey.PublicKey; + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricBcEcDiffieHellmanPrivateKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPublicKey.cs new file mode 100644 index 0000000..88bf370 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPublicKey.cs @@ -0,0 +1,56 @@ +using Org.BouncyCastle.Crypto.Parameters; + +namespace wan24.Crypto.BC +{ + /// + /// EC Diffie Hellman asymmetric public key + /// + public sealed record class AsymmetricBcEcDiffieHellmanPublicKey + : BouncyCastleAsymmetricNonPqcPublicKeyBase + { + /// + /// Constructor + /// + public AsymmetricBcEcDiffieHellmanPublicKey() : base(AsymmetricBcEcDiffieHellmanAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricBcEcDiffieHellmanPublicKey(byte[] keyData) : base(AsymmetricBcEcDiffieHellmanAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Public key + public AsymmetricBcEcDiffieHellmanPublicKey(ECPublicKeyParameters publicKey) : base(AsymmetricBcEcDiffieHellmanAlgorithm.ALGORITHM_NAME, publicKey) { } + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + if(_PublicKey is null) throw new InvalidOperationException(); + return BcEllipticCurves.GetKeySize(_PublicKey.Parameters); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricBcEcDiffieHellmanPublicKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricBcEcDsaAlgorithm.cs b/src/wan24-Crypto-BC/AsymmetricBcEcDsaAlgorithm.cs new file mode 100644 index 0000000..46c42b0 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricBcEcDsaAlgorithm.cs @@ -0,0 +1,76 @@ +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using System.Collections.ObjectModel; + +namespace wan24.Crypto.BC +{ + /// + /// EC DSA asymmetric algorithm (may be used as replacement, if the .NET algorithm isn't available on the current platform) + /// + public sealed record class AsymmetricBcEcDsaAlgorithm + : BouncyCastleAsymmetricAlgorithmBase< + AsymmetricBcEcDsaPublicKey, + AsymmetricBcEcDsaPrivateKey, + ECKeyPairGenerator, + ECKeyGenerationParameters, + ECDomainParameters, + ECPublicKeyParameters, + ECPrivateKeyParameters, + AsymmetricBcEcDsaAlgorithm + > + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "ECDSA"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 1; + /// + /// Default key size in bits + /// + public const int DEFAULT_KEY_SIZE = 521; + /// + /// Algorithm usages + /// + public const AsymmetricAlgorithmUsages USAGES = AsymmetricAlgorithmUsages.Signature; + /// + /// Display name + /// + public const string DISPLAY_NAME = "EC DSA"; + + /// + /// Allowed key sizes in bits + /// + private static readonly ReadOnlyCollection _AllowedKeySizes; + + /// + /// Static constructor + /// + static AsymmetricBcEcDsaAlgorithm() => _AllowedKeySizes = new List() + { + 256, + 384, + 521 + }.AsReadOnly(); + + /// + /// Constructor + /// + public AsymmetricBcEcDsaAlgorithm() + : base(ALGORITHM_NAME, ALGORITHM_VALUE, USAGES, isEllipticCurveAlgorithm: true, _AllowedKeySizes, isPostQuantum: true, DEFAULT_KEY_SIZE) + { } + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + protected override ECKeyGenerationParameters CreateKeyGenParameters(SecureRandom random, ECDomainParameters parameters, CryptoOptions options) + => new(parameters, random); + + /// + protected override ECDomainParameters GetEngineParameters(CryptoOptions options) => BcEllipticCurves.GetCurve(options.AsymmetricKeyBits); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricBcEcDsaPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricBcEcDsaPrivateKey.cs new file mode 100644 index 0000000..8a345d7 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricBcEcDsaPrivateKey.cs @@ -0,0 +1,87 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// Asymmetric EC DSA private key + /// + public sealed record class AsymmetricBcEcDsaPrivateKey + : BouncyCastleAsymmetricNonPqcPrivateKeyBase< + AsymmetricBcEcDsaPublicKey, + AsymmetricBcEcDsaAlgorithm, + ECPublicKeyParameters, + ECPrivateKeyParameters, + AsymmetricBcEcDsaPrivateKey + >, ISignaturePrivateKey + { + /// + /// Constructor + /// + public AsymmetricBcEcDsaPrivateKey() : base(AsymmetricBcEcDsaAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricBcEcDsaPrivateKey(byte[] keyData) : base(AsymmetricBcEcDsaAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Keys + public AsymmetricBcEcDsaPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricBcEcDsaAlgorithm.ALGORITHM_NAME, keys) { } + + /// + protected override ECPublicKeyParameters GetPublicKey(ECPrivateKeyParameters privateKey) => new(privateKey.Parameters.G.Multiply(privateKey.D), privateKey.Parameters); + + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (Keys?.Private is not ECPrivateKeyParameters privateKey) return; + privateKey.D.ClearPrivateByteArrayFields();//TODO All fields are private :( + } + + /// + protected override async Task DisposeCore() + { + await base.DisposeCore().DynamicContext(); + if (Keys?.Private is not ECPrivateKeyParameters privateKey) return; + privateKey.D.ClearPrivateByteArrayFields();//TODO All fields are private :( + } + + /// + public override byte[] SignHashRaw(byte[] hash) + { + try + { + EnsureUndisposed(); + if (CryptoHelper.StrictPostQuantumSafety) throw new InvalidOperationException($"Post quantum safety-forced - {Algorithm.Name} isn't post quantum"); + DsaDigestSigner signer = new(new ECDsaSigner(), new NullDigest()); + signer.Init(forSigning: true, PrivateKey); + signer.BlockUpdate(hash); + return signer.GenerateSignature(); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + /// Cast to public key + /// + /// Private key + public static implicit operator AsymmetricBcEcDsaPublicKey(AsymmetricBcEcDsaPrivateKey privateKey) => privateKey.PublicKey; + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricBcEcDsaPrivateKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricBcEcDsaPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricBcEcDsaPublicKey.cs new file mode 100644 index 0000000..ea5ba14 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricBcEcDsaPublicKey.cs @@ -0,0 +1,101 @@ +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; + +namespace wan24.Crypto.BC +{ + /// + /// Asymmetric EC DSA public key + /// + public sealed record class AsymmetricBcEcDsaPublicKey + : BouncyCastleAsymmetricNonPqcPublicKeyBase, ISignaturePublicKey + { + /// + /// Constructor + /// + public AsymmetricBcEcDsaPublicKey() : base(AsymmetricBcEcDsaAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricBcEcDsaPublicKey(byte[] keyData) : base(AsymmetricBcEcDsaAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Public key + public AsymmetricBcEcDsaPublicKey(ECPublicKeyParameters publicKey) : base(AsymmetricBcEcDsaAlgorithm.ALGORITHM_NAME, publicKey) { } + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + if (_PublicKey is null) throw new InvalidOperationException(); + return BcEllipticCurves.GetKeySize(_PublicKey.Parameters); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + public override bool ValidateSignatureRaw(byte[] signature, byte[] signedHash, bool throwOnError = true) + { + try + { + EnsureUndisposed(); + DsaDigestSigner signer = new(new ECDsaSigner(), new NullDigest()); + signer.Init(forSigning: false, PublicKey); + signer.BlockUpdate(signedHash); + bool res = signer.VerifySignature(signature); + if (!res && throwOnError) throw new InvalidDataException("Signature validation failed"); + return res; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override bool ValidateSignatureInt(SignatureContainer signature, bool throwOnError = true) + { + try + { + EnsureUndisposed(); + bool res = ValidateSignatureRaw(signature.Signature, signature.CreateSignatureHash()); + if (!res && throwOnError) throw new InvalidDataException("Signature validation failed"); + return res; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricBcEcDsaPublicKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricDilithiumPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricDilithiumPrivateKey.cs index 0ddc60e..2750eff 100644 --- a/src/wan24-Crypto-BC/AsymmetricDilithiumPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricDilithiumPrivateKey.cs @@ -8,7 +8,7 @@ namespace wan24.Crypto.BC /// CRYSTALS-Dilithium asymmetric private key /// public sealed record class AsymmetricDilithiumPrivateKey - : BouncyCastleAsymmetricPrivateSignatureKeyBase< + : BouncyCastleAsymmetricPqcPrivateSignatureKeyBase< AsymmetricDilithiumPublicKey, AsymmetricDilithiumAlgorithm, DilithiumPublicKeyParameters, @@ -34,34 +34,21 @@ public AsymmetricDilithiumPrivateKey(byte[] keyData) : base(AsymmetricDilithiumA /// Keys public AsymmetricDilithiumPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricDilithiumAlgorithm.ALGORITHM_NAME, keys) { } + /// + protected override DilithiumPublicKeyParameters GetPublicKey(DilithiumPrivateKeyParameters privateKey) => new(privateKey.Parameters, privateKey.Rho, privateKey.T1); + /// protected override void Dispose(bool disposing) { base.Dispose(disposing); - if (Keys == null) return; - DilithiumPrivateKeyParameters privateKey = (DilithiumPrivateKeyParameters)Keys.Private; - privateKey.K.Clear(); - privateKey.Rho.Clear(); - privateKey.S1.Clear(); - privateKey.S2.Clear(); - privateKey.T0.Clear(); - privateKey.T1.Clear(); - privateKey.Tr.Clear(); + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( } /// protected override async Task DisposeCore() { await base.DisposeCore().DynamicContext(); - if (Keys == null) return; - DilithiumPrivateKeyParameters privateKey = (DilithiumPrivateKeyParameters)Keys.Private; - privateKey.K.Clear(); - privateKey.Rho.Clear(); - privateKey.S1.Clear(); - privateKey.S2.Clear(); - privateKey.T0.Clear(); - privateKey.T1.Clear(); - privateKey.Tr.Clear(); + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( } /// diff --git a/src/wan24-Crypto-BC/AsymmetricDilithiumPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricDilithiumPublicKey.cs index cd1f70b..f73472c 100644 --- a/src/wan24-Crypto-BC/AsymmetricDilithiumPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricDilithiumPublicKey.cs @@ -6,7 +6,7 @@ namespace wan24.Crypto.BC /// CRYSTALS-Dilithium asymmetric public key /// public sealed record class AsymmetricDilithiumPublicKey - : BouncyCastleAsymmetricPublicSignatureKeyBase + : BouncyCastleAsymmetricPqcPublicSignatureKeyBase { /// /// Constructor diff --git a/src/wan24-Crypto-BC/AsymmetricEd25519Algorithm.cs b/src/wan24-Crypto-BC/AsymmetricEd25519Algorithm.cs new file mode 100644 index 0000000..803d05b --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricEd25519Algorithm.cs @@ -0,0 +1,94 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using System.Collections.ObjectModel; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// Ed25519 asymmetric algorithm + /// + public sealed record class AsymmetricEd25519Algorithm + : BouncyCastleAsymmetricAlgorithmBase< + AsymmetricEd25519PublicKey, + AsymmetricEd25519PrivateKey, + Ed25519KeyPairGenerator, + Ed25519KeyGenerationParameters, + AsymmetricKeyParameter, + Ed25519PublicKeyParameters, + Ed25519PrivateKeyParameters, + AsymmetricEd25519Algorithm + > + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "ED25519"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 8; + /// + /// Default key size in bits + /// + public const int DEFAULT_KEY_SIZE = 256; + /// + /// Algorithm usages + /// + public const AsymmetricAlgorithmUsages USAGES = AsymmetricAlgorithmUsages.Signature; + /// + /// Display name + /// + public const string DISPLAY_NAME = "Ed25519"; + + /// + /// Allowed key sizes in bits + /// + private static readonly ReadOnlyCollection _AllowedKeySizes; + + /// + /// Static constructor + /// + static AsymmetricEd25519Algorithm() => _AllowedKeySizes = new List() + { + 256 + }.AsReadOnly(); + + /// + /// Constructor + /// + public AsymmetricEd25519Algorithm() + : base(ALGORITHM_NAME, ALGORITHM_VALUE, USAGES, isEllipticCurveAlgorithm: true, _AllowedKeySizes, isPostQuantum: false, DEFAULT_KEY_SIZE) + { } + + + /// + public override AsymmetricEd25519PrivateKey CreateKeyPair(CryptoOptions? options = null) + { + try + { + options ??= DefaultOptions; + if (!options.AsymmetricKeyBits.In(AllowedKeySizes)) throw new ArgumentException("Invalid key size", nameof(options)); + Ed25519KeyPairGenerator keyGen = new(); + keyGen.Init(new Ed25519KeyGenerationParameters(new SecureRandom(BouncyCastleRandomGenerator.Instance()))); + return new(keyGen.GenerateKeyPair()); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + protected override AsymmetricKeyParameter GetEngineParameters(CryptoOptions options) => throw new NotSupportedException(); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricEd25519PrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricEd25519PrivateKey.cs new file mode 100644 index 0000000..df8f747 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricEd25519PrivateKey.cs @@ -0,0 +1,67 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// Ed25519 asymmetric private key + /// + public sealed record class AsymmetricEd25519PrivateKey + : BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase< + AsymmetricEd25519PublicKey, + AsymmetricEd25519Algorithm, + Ed25519PublicKeyParameters, + Ed25519PrivateKeyParameters, + Ed25519Signer, + AsymmetricEd25519PrivateKey + > + { + /// + /// Constructor + /// + public AsymmetricEd25519PrivateKey() : base(AsymmetricEd25519Algorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricEd25519PrivateKey(byte[] keyData) : base(AsymmetricEd25519Algorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Keys + public AsymmetricEd25519PrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricEd25519Algorithm.ALGORITHM_NAME, keys) { } + + /// + protected override Ed25519PublicKeyParameters GetPublicKey(Ed25519PrivateKeyParameters privateKey) => privateKey.GeneratePublicKey(); + + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + Keys?.Private.ClearPrivateByteArrayFields(); + } + + /// + protected override async Task DisposeCore() + { + await base.DisposeCore().DynamicContext(); + Keys?.Private.ClearPrivateByteArrayFields(); + } + + /// + /// Cast to public key + /// + /// Private key + public static implicit operator AsymmetricEd25519PublicKey(AsymmetricEd25519PrivateKey privateKey) => privateKey.PublicKey; + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricEd25519PrivateKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricEd25519PublicKey.cs b/src/wan24-Crypto-BC/AsymmetricEd25519PublicKey.cs new file mode 100644 index 0000000..72b3558 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricEd25519PublicKey.cs @@ -0,0 +1,56 @@ +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; + +namespace wan24.Crypto.BC +{ + /// + /// Ed25519 asymmetric public key + /// + public sealed record class AsymmetricEd25519PublicKey + : BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase + { + /// + /// Constructor + /// + public AsymmetricEd25519PublicKey() : base(AsymmetricEd25519Algorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricEd25519PublicKey(byte[] keyData) : base(AsymmetricEd25519Algorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Public key + public AsymmetricEd25519PublicKey(Ed25519PublicKeyParameters publicKey) : base(AsymmetricEd25519Algorithm.ALGORITHM_NAME, publicKey) { } + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + return 256; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricEd25519PublicKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricEd448Algorithm.cs b/src/wan24-Crypto-BC/AsymmetricEd448Algorithm.cs new file mode 100644 index 0000000..7a27d31 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricEd448Algorithm.cs @@ -0,0 +1,95 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using System.Collections.ObjectModel; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// Ed448 asymmetric algorithm + /// + public sealed record class AsymmetricEd448Algorithm + : BouncyCastleAsymmetricAlgorithmBase< + AsymmetricEd448PublicKey, + AsymmetricEd448PrivateKey, + Ed448KeyPairGenerator, + Ed448KeyGenerationParameters, + AsymmetricKeyParameter, + Ed448PublicKeyParameters, + Ed448PrivateKeyParameters, + AsymmetricEd448Algorithm + > + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "ED448"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 9; + /// + /// Default key size in bits + /// + public const int DEFAULT_KEY_SIZE = 456; + /// + /// Algorithm usages + /// + public const AsymmetricAlgorithmUsages USAGES = AsymmetricAlgorithmUsages.Signature; + /// + /// Display name + /// + public const string DISPLAY_NAME = "edwards448-Goldilocks"; + + /// + /// Allowed key sizes in bits + /// + private static readonly ReadOnlyCollection _AllowedKeySizes; + + /// + /// Static constructor + /// + static AsymmetricEd448Algorithm() => _AllowedKeySizes = new List() + { + 448, + 456 + }.AsReadOnly(); + + /// + /// Constructor + /// + public AsymmetricEd448Algorithm() + : base(ALGORITHM_NAME, ALGORITHM_VALUE, USAGES, isEllipticCurveAlgorithm: true, _AllowedKeySizes, isPostQuantum: false, DEFAULT_KEY_SIZE) + { } + + + /// + public override AsymmetricEd448PrivateKey CreateKeyPair(CryptoOptions? options = null) + { + try + { + options ??= DefaultOptions; + if (!options.AsymmetricKeyBits.In(AllowedKeySizes)) throw new ArgumentException("Invalid key size", nameof(options)); + Ed448KeyPairGenerator keyGen = new(); + keyGen.Init(new Ed448KeyGenerationParameters(new SecureRandom(BouncyCastleRandomGenerator.Instance()))); + return new(keyGen.GenerateKeyPair()); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + protected override AsymmetricKeyParameter GetEngineParameters(CryptoOptions options) => throw new NotSupportedException(); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricEd448PrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricEd448PrivateKey.cs new file mode 100644 index 0000000..a943a91 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricEd448PrivateKey.cs @@ -0,0 +1,89 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Tls.Crypto; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// Ed448 asymmetric private key + /// + public sealed record class AsymmetricEd448PrivateKey + : BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2< + AsymmetricEd448PublicKey, + AsymmetricEd448Algorithm, + Ed448PublicKeyParameters, + Ed448PrivateKeyParameters, + Ed448Signer, + AsymmetricEd448PrivateKey + > + { + /// + /// Constructor + /// + public AsymmetricEd448PrivateKey() : base(AsymmetricEd448Algorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricEd448PrivateKey(byte[] keyData) : base(AsymmetricEd448Algorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Keys + public AsymmetricEd448PrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricEd448Algorithm.ALGORITHM_NAME, keys) { } + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + return 456; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + protected override Ed448PublicKeyParameters GetPublicKey(Ed448PrivateKeyParameters privateKey) => privateKey.GeneratePublicKey(); + + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + Keys?.Private.ClearPrivateByteArrayFields(); + } + + /// + protected override async Task DisposeCore() + { + await base.DisposeCore().DynamicContext(); + Keys?.Private.ClearPrivateByteArrayFields(); + } + + /// + /// Cast to public key + /// + /// Private key + public static implicit operator AsymmetricEd448PublicKey(AsymmetricEd448PrivateKey privateKey) => privateKey.PublicKey; + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricEd448PrivateKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricEd448PublicKey.cs b/src/wan24-Crypto-BC/AsymmetricEd448PublicKey.cs new file mode 100644 index 0000000..4e27d92 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricEd448PublicKey.cs @@ -0,0 +1,56 @@ +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; + +namespace wan24.Crypto.BC +{ + /// + /// Ed448 asymmetric public key + /// + public sealed record class AsymmetricEd448PublicKey + : BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase2 + { + /// + /// Constructor + /// + public AsymmetricEd448PublicKey() : base(AsymmetricEd448Algorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricEd448PublicKey(byte[] keyData) : base(AsymmetricEd448Algorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Public key + public AsymmetricEd448PublicKey(Ed448PublicKeyParameters publicKey) : base(AsymmetricEd448Algorithm.ALGORITHM_NAME, publicKey) { } + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + return 448; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricEd448PublicKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricFalconPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricFalconPrivateKey.cs index b95dbda..2d30f11 100644 --- a/src/wan24-Crypto-BC/AsymmetricFalconPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricFalconPrivateKey.cs @@ -1,5 +1,6 @@ using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Pqc.Crypto.Falcon; +using Org.BouncyCastle.Pqc.Crypto.Utilities; using wan24.Core; namespace wan24.Crypto.BC @@ -8,7 +9,7 @@ namespace wan24.Crypto.BC /// Asymmetric FALCON private key /// public sealed record class AsymmetricFalconPrivateKey - : BouncyCastleAsymmetricPrivateSignatureKeyBase< + : BouncyCastleAsymmetricPqcPrivateSignatureKeyBase< AsymmetricFalconPublicKey, AsymmetricFalconAlgorithm, FalconPublicKeyParameters, @@ -34,20 +35,21 @@ public AsymmetricFalconPrivateKey(byte[] keyData) : base(AsymmetricFalconAlgorit /// Keys public AsymmetricFalconPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricFalconAlgorithm.ALGORITHM_NAME, keys) { } + /// + protected override FalconPublicKeyParameters GetPublicKey(FalconPrivateKeyParameters privateKey) => new(privateKey.Parameters, privateKey.GetPublicKey()); + /// protected override void Dispose(bool disposing) { base.Dispose(disposing); - if (Keys == null) return; - Keys.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( } /// protected override async Task DisposeCore() { await base.DisposeCore().DynamicContext(); - if (Keys == null) return; - Keys.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( } /// diff --git a/src/wan24-Crypto-BC/AsymmetricFalconPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricFalconPublicKey.cs index 0737824..f54b369 100644 --- a/src/wan24-Crypto-BC/AsymmetricFalconPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricFalconPublicKey.cs @@ -5,7 +5,7 @@ namespace wan24.Crypto.BC /// /// Asymmetric FALCON public key /// - public sealed record class AsymmetricFalconPublicKey : BouncyCastleAsymmetricPublicSignatureKeyBase + public sealed record class AsymmetricFalconPublicKey : BouncyCastleAsymmetricPqcPublicSignatureKeyBase { /// /// Constructor diff --git a/src/wan24-Crypto-BC/AsymmetricFrodoKemPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricFrodoKemPrivateKey.cs index cb153cf..5292bc6 100644 --- a/src/wan24-Crypto-BC/AsymmetricFrodoKemPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricFrodoKemPrivateKey.cs @@ -1,14 +1,16 @@ using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Pqc.Crypto.Frodo; +using Org.BouncyCastle.Pqc.Crypto.Utilities; using wan24.Core; +using wan24.StreamSerializerExtensions; namespace wan24.Crypto.BC { /// - /// FrodoKEMasymmetric private key + /// FrodoKEM asymmetric private key /// public sealed record class AsymmetricFrodoKemPrivateKey - : BouncyCastleAsymmetricPrivateKeyExchangeKeyBase< + : BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase< AsymmetricFrodoKemPublicKey, AsymmetricFrodoKemAlgorithm, FrodoPublicKeyParameters, @@ -35,20 +37,84 @@ public AsymmetricFrodoKemPrivateKey(byte[] keyData) : base(AsymmetricFrodoKemAlg /// Keys public AsymmetricFrodoKemPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricFrodoKemAlgorithm.ALGORITHM_NAME, keys) { } + /// + protected override byte[] SerializeKeyData() + { + try + { + EnsureUndisposed(); + if (Keys == null) throw new InvalidOperationException(); + using MemoryPoolStream ms = new() + { + CleanReturned = true + }; + using SecureByteArray privateKey = new((Keys.Private as FrodoPrivateKeyParameters)!.GetPrivateKey()); + using SecureByteArray publicKey = new((Keys.Public as FrodoPublicKeyParameters)!.GetPublicKey()); + ms.WriteSerializerVersion() + .WriteNumber(Bits) + .WriteBytes(privateKey.Array) + .WriteBytes(publicKey.Array); + return ms.ToArray(); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override void DeserializeKeyData() + { + try + { + EnsureUndisposed(); + FrodoPrivateKeyParameters? privateKey = null; + FrodoPublicKeyParameters? publicKey = null; + try + { + using MemoryStream ms = new(KeyData.Array); + int ssv = ms.ReadSerializerVersion(); + FrodoParameters param = AsymmetricFrodoKemHelper.GetParameters(ms.ReadNumber(ssv)); + privateKey = new(param, ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); + publicKey = new(param, ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); + Keys = new(publicKey, privateKey); + } + catch + { + privateKey?.ClearPrivateByteArrayFields(); + publicKey?.ClearPrivateByteArrayFields(); + throw; + } + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override FrodoPublicKeyParameters GetPublicKey(FrodoPrivateKeyParameters privateKey) => throw new NotSupportedException(); + /// protected override void Dispose(bool disposing) { base.Dispose(disposing); - if (Keys == null) return; - Keys.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( } /// protected override async Task DisposeCore() { await base.DisposeCore().DynamicContext(); - if (Keys == null) return; - Keys.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( } /// diff --git a/src/wan24-Crypto-BC/AsymmetricFrodoKemPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricFrodoKemPublicKey.cs index 4528f1c..f2ddcaa 100644 --- a/src/wan24-Crypto-BC/AsymmetricFrodoKemPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricFrodoKemPublicKey.cs @@ -1,11 +1,14 @@ using Org.BouncyCastle.Pqc.Crypto.Frodo; +using Org.BouncyCastle.Pqc.Crypto.Utilities; +using wan24.Core; +using wan24.StreamSerializerExtensions; namespace wan24.Crypto.BC { /// /// FrodoKEM asymmetric public key /// - public sealed record class AsymmetricFrodoKemPublicKey : BouncyCastleAsymmetricPublicKeyBase + public sealed record class AsymmetricFrodoKemPublicKey : BouncyCastleAsymmetricPqcPublicKeyBase { /// /// Constructor @@ -45,6 +48,54 @@ public override int Bits } } + /// + protected override byte[] SerializeKeyData() + { + try + { + EnsureUndisposed(); + if (_PublicKey == null) throw new InvalidOperationException(); + using MemoryPoolStream ms = new() + { + CleanReturned = true + }; + using SecureByteArray publicKey = new(_PublicKey.GetPublicKey()); + ms.WriteSerializerVersion() + .WriteNumber(Bits) + .WriteBytes(publicKey.Array); + return ms.ToArray(); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override void DeserializeKeyData() + { + try + { + EnsureUndisposed(); + using MemoryStream ms = new(KeyData.Array); + int ssv = ms.ReadSerializerVersion(); + FrodoParameters param = AsymmetricFrodoKemHelper.GetParameters(ms.ReadNumber(ssv)); + _PublicKey = new(param, ms.ReadBytes(ssv, minLen: 1, maxLen: ushort.MaxValue).Value); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + /// /// Cast from serialized data /// diff --git a/src/wan24-Crypto-BC/AsymmetricKyberPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricKyberPrivateKey.cs index 4dd75bd..4ad6038 100644 --- a/src/wan24-Crypto-BC/AsymmetricKyberPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricKyberPrivateKey.cs @@ -8,7 +8,7 @@ namespace wan24.Crypto.BC /// CRYSTALS-Kyber asymmetric private key /// public sealed record class AsymmetricKyberPrivateKey - : BouncyCastleAsymmetricPrivateKeyExchangeKeyBase< + : BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase< AsymmetricKyberPublicKey, AsymmetricKyberAlgorithm, KyberPublicKeyParameters, @@ -35,20 +35,27 @@ public AsymmetricKyberPrivateKey(byte[] keyData) : base(AsymmetricKyberAlgorithm /// Keys public AsymmetricKyberPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricKyberAlgorithm.ALGORITHM_NAME, keys) { } + /// + protected override byte[] SerializeKeyData() => SerializeFullKeyData(); + + /// + protected override void DeserializeKeyData() => DeserializeFullKeyData(); + + /// + protected override KyberPublicKeyParameters GetPublicKey(KyberPrivateKeyParameters privateKey) => throw new NotSupportedException(); + /// protected override void Dispose(bool disposing) { base.Dispose(disposing); - if (Keys == null) return; - Keys.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( } /// protected override async Task DisposeCore() { await base.DisposeCore().DynamicContext(); - if (Keys == null) return; - Keys.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( } /// diff --git a/src/wan24-Crypto-BC/AsymmetricKyberPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricKyberPublicKey.cs index a33994f..3ae7304 100644 --- a/src/wan24-Crypto-BC/AsymmetricKyberPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricKyberPublicKey.cs @@ -5,7 +5,7 @@ namespace wan24.Crypto.BC /// /// CRYSTALS-Kyber asymmetric public key /// - public sealed record class AsymmetricKyberPublicKey : BouncyCastleAsymmetricPublicKeyBase + public sealed record class AsymmetricKyberPublicKey : BouncyCastleAsymmetricPqcPublicKeyBase { /// /// Constructor diff --git a/src/wan24-Crypto-BC/AsymmetricNtruEncryptPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricNtruEncryptPrivateKey.cs index 944559b..977a4f5 100644 --- a/src/wan24-Crypto-BC/AsymmetricNtruEncryptPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricNtruEncryptPrivateKey.cs @@ -1,6 +1,7 @@ using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Pqc.Crypto.Ntru; using wan24.Core; +using wan24.StreamSerializerExtensions; namespace wan24.Crypto.BC { @@ -8,7 +9,7 @@ namespace wan24.Crypto.BC /// NTRUEncrypt asymmetric private key /// public sealed record class AsymmetricNtruEncryptPrivateKey - : BouncyCastleAsymmetricPrivateKeyExchangeKeyBase< + : BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase< AsymmetricNtruEncryptPublicKey, AsymmetricNtruEncryptAlgorithm, NtruPublicKeyParameters, @@ -35,20 +36,84 @@ public AsymmetricNtruEncryptPrivateKey(byte[] keyData) : base(AsymmetricNtruEncr /// Keys public AsymmetricNtruEncryptPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricNtruEncryptAlgorithm.ALGORITHM_NAME, keys) { } + /// + protected override byte[] SerializeKeyData() + { + try + { + EnsureUndisposed(); + if (Keys == null) throw new InvalidOperationException(); + using MemoryPoolStream ms = new() + { + CleanReturned = true + }; + using SecureByteArray privateKey = new((Keys.Private as NtruPrivateKeyParameters)!.PrivateKey); + using SecureByteArray publicKey = new((Keys.Public as NtruPublicKeyParameters)!.PublicKey); + ms.WriteSerializerVersion() + .WriteNumber(Bits) + .WriteBytes(privateKey.Array) + .WriteBytes(publicKey.Array); + return ms.ToArray(); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override void DeserializeKeyData() + { + try + { + EnsureUndisposed(); + NtruPrivateKeyParameters? privateKey = null; + NtruPublicKeyParameters? publicKey = null; + try + { + using MemoryStream ms = new(KeyData.Array); + int ssv = ms.ReadSerializerVersion(); + NtruParameters param = AsymmetricNtruHelper.GetParameters(ms.ReadNumber(ssv)); + privateKey = new(param, ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); + publicKey = new(param, ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); + Keys = new(publicKey, privateKey); + } + catch + { + privateKey?.ClearPrivateByteArrayFields(); + publicKey?.ClearPrivateByteArrayFields(); + throw; + } + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override NtruPublicKeyParameters GetPublicKey(NtruPrivateKeyParameters privateKey) => throw new NotSupportedException(); + /// protected override void Dispose(bool disposing) { base.Dispose(disposing); - if (Keys == null) return; - Keys.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( } /// protected override async Task DisposeCore() { await base.DisposeCore().DynamicContext(); - if (Keys == null) return; - Keys.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( } /// diff --git a/src/wan24-Crypto-BC/AsymmetricNtruEncryptPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricNtruEncryptPublicKey.cs index b4330f8..12ab9fc 100644 --- a/src/wan24-Crypto-BC/AsymmetricNtruEncryptPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricNtruEncryptPublicKey.cs @@ -1,4 +1,6 @@ using Org.BouncyCastle.Pqc.Crypto.Ntru; +using wan24.Core; +using wan24.StreamSerializerExtensions; namespace wan24.Crypto.BC { @@ -6,7 +8,7 @@ namespace wan24.Crypto.BC /// NTRUEncrypt asymmetric public key /// public sealed record class AsymmetricNtruEncryptPublicKey - : BouncyCastleAsymmetricPublicKeyBase + : BouncyCastleAsymmetricPqcPublicKeyBase { /// /// Constructor @@ -46,6 +48,54 @@ public override int Bits } } + /// + protected override byte[] SerializeKeyData() + { + try + { + EnsureUndisposed(); + if (_PublicKey == null) throw new InvalidOperationException(); + using MemoryPoolStream ms = new() + { + CleanReturned = true + }; + using SecureByteArray publicKey = new(_PublicKey.PublicKey); + ms.WriteSerializerVersion() + .WriteNumber(Bits) + .WriteBytes(publicKey.Array); + return ms.ToArray(); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override void DeserializeKeyData() + { + try + { + EnsureUndisposed(); + using MemoryStream ms = new(KeyData.Array); + int ssv = ms.ReadSerializerVersion(); + NtruParameters param = AsymmetricNtruHelper.GetParameters(ms.ReadNumber(ssv)); + _PublicKey = new(param, ms.ReadBytes(ssv, minLen: 1, maxLen: ushort.MaxValue).Value); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + /// /// Cast from serialized data /// diff --git a/src/wan24-Crypto-BC/AsymmetricSphincsPlusPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricSphincsPlusPrivateKey.cs index 81d46b5..e9ef501 100644 --- a/src/wan24-Crypto-BC/AsymmetricSphincsPlusPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricSphincsPlusPrivateKey.cs @@ -1,5 +1,7 @@ using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Pqc.Crypto.SphincsPlus; +using Org.BouncyCastle.Pqc.Crypto.Utilities; +using System.Reflection; using wan24.Core; namespace wan24.Crypto.BC @@ -8,7 +10,7 @@ namespace wan24.Crypto.BC /// SPHINCS+ asymmetric private key /// public sealed record class AsymmetricSphincsPlusPrivateKey - : BouncyCastleAsymmetricPrivateSignatureKeyBase< + : BouncyCastleAsymmetricPqcPrivateSignatureKeyBase< AsymmetricSphincsPlusPublicKey, AsymmetricSphincsPlusAlgorithm, SphincsPlusPublicKeyParameters, @@ -34,20 +36,27 @@ public AsymmetricSphincsPlusPrivateKey(byte[] keyData) : base(AsymmetricSphincsP /// Keys public AsymmetricSphincsPlusPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricSphincsPlusAlgorithm.ALGORITHM_NAME, keys) { } + /// + protected override SphincsPlusPublicKeyParameters GetPublicKey(SphincsPlusPrivateKeyParameters privateKey) => new(privateKey.Parameters, privateKey.GetPublicKey()); + /// protected override void Dispose(bool disposing) { base.Dispose(disposing); - if (Keys == null) return; - Keys.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + if (Keys is null) return; + //TODO All parameter fields are private :( + typeof(SphincsPlusPrivateKeyParameters).GetFieldCached("m_sk", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(Keys.Private)!.ClearPrivateByteArrayFields(); + typeof(SphincsPlusPrivateKeyParameters).GetFieldCached("m_pk", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(Keys.Private)!.ClearPrivateByteArrayFields(); } /// protected override async Task DisposeCore() { await base.DisposeCore().DynamicContext(); - if (Keys == null) return; - Keys.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + if (Keys is null) return; + //TODO All parameter fields are private :( + typeof(SphincsPlusPrivateKeyParameters).GetFieldCached("m_sk", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(Keys.Private)!.ClearPrivateByteArrayFields(); + typeof(SphincsPlusPrivateKeyParameters).GetFieldCached("m_pk", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(Keys.Private)!.ClearPrivateByteArrayFields(); } /// diff --git a/src/wan24-Crypto-BC/AsymmetricSphincsPlusPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricSphincsPlusPublicKey.cs index 8fc2085..ba68123 100644 --- a/src/wan24-Crypto-BC/AsymmetricSphincsPlusPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricSphincsPlusPublicKey.cs @@ -6,7 +6,7 @@ namespace wan24.Crypto.BC /// SPHINCS+ asymmetric public key /// public sealed record class AsymmetricSphincsPlusPublicKey - : BouncyCastleAsymmetricPublicSignatureKeyBase + : BouncyCastleAsymmetricPqcPublicSignatureKeyBase { /// /// Constructor diff --git a/src/wan24-Crypto-BC/AsymmetricX25519Algorithm.cs b/src/wan24-Crypto-BC/AsymmetricX25519Algorithm.cs new file mode 100644 index 0000000..9f7b40d --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricX25519Algorithm.cs @@ -0,0 +1,93 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using System.Collections.ObjectModel; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// X25519 asymmetric algorithm + /// + public sealed record class AsymmetricX25519Algorithm + : BouncyCastleAsymmetricAlgorithmBase< + AsymmetricX25519PublicKey, + AsymmetricX25519PrivateKey, + X25519KeyPairGenerator, + X25519KeyGenerationParameters, + AsymmetricKeyParameter, + X25519PublicKeyParameters, + X25519PrivateKeyParameters, + AsymmetricX25519Algorithm + > + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "X25519"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 10; + /// + /// Default key size in bits + /// + public const int DEFAULT_KEY_SIZE = 256; + /// + /// Algorithm usages + /// + public const AsymmetricAlgorithmUsages USAGES = AsymmetricAlgorithmUsages.KeyExchange; + /// + /// Display name + /// + public const string DISPLAY_NAME = "X25519"; + + /// + /// Allowed key sizes in bits + /// + private static readonly ReadOnlyCollection _AllowedKeySizes; + + /// + /// Static constructor + /// + static AsymmetricX25519Algorithm() => _AllowedKeySizes = new List() + { + 256 + }.AsReadOnly(); + + /// + /// Constructor + /// + public AsymmetricX25519Algorithm() + : base(ALGORITHM_NAME, ALGORITHM_VALUE, USAGES, isEllipticCurveAlgorithm: true, _AllowedKeySizes, isPostQuantum: false, DEFAULT_KEY_SIZE) + { } + + /// + public override AsymmetricX25519PrivateKey CreateKeyPair(CryptoOptions? options = null) + { + try + { + options ??= DefaultOptions; + if (!options.AsymmetricKeyBits.In(AllowedKeySizes)) throw new ArgumentException("Invalid key size", nameof(options)); + X25519KeyPairGenerator keyGen = new(); + keyGen.Init(new X25519KeyGenerationParameters(new SecureRandom(BouncyCastleRandomGenerator.Instance()))); + return new(keyGen.GenerateKeyPair()); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + protected override AsymmetricKeyParameter GetEngineParameters(CryptoOptions options) => throw new NotSupportedException(); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricX25519PrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricX25519PrivateKey.cs new file mode 100644 index 0000000..9149bb7 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricX25519PrivateKey.cs @@ -0,0 +1,127 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Parameters; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// X25519 asymmetric private key + /// + public sealed record class AsymmetricX25519PrivateKey + : BouncyCastleAsymmetricNonPqcPrivateKeyBase< + AsymmetricX25519PublicKey, + AsymmetricX25519Algorithm, + X25519PublicKeyParameters, + X25519PrivateKeyParameters, + AsymmetricX25519PrivateKey + >, IKeyExchangePrivateKey + { + /// + /// Constructor + /// + public AsymmetricX25519PrivateKey() : base(AsymmetricX25519Algorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricX25519PrivateKey(byte[] keyData) : base(AsymmetricX25519Algorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Keys + public AsymmetricX25519PrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricX25519Algorithm.ALGORITHM_NAME, keys) { } + + /// + public override (byte[] Key, byte[] KeyExchangeData) GetKeyExchangeData(IAsymmetricPublicKey? publicKey = null, CryptoOptions? options = null) + { + try + { + EnsureUndisposed(); + if (CryptoHelper.StrictPostQuantumSafety) throw new InvalidOperationException($"Post quantum safety-forced - {Algorithm.Name} isn't post quantum"); + publicKey ??= options?.PublicKey ?? options?.PrivateKey?.PublicKey ?? PublicKey; + if (publicKey is not AsymmetricX25519PublicKey key) throw new ArgumentException($"Public {Algorithm.Name} key required", nameof(publicKey)); + return (DeriveKey(publicKey), PublicKey.KeyData.Array.CloneArray()); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + public override byte[] DeriveKey(byte[] keyExchangeData) + { + try + { + EnsureUndisposed(); + if (CryptoHelper.StrictPostQuantumSafety) throw new InvalidOperationException($"Post quantum safety-forced - {Algorithm.Name} isn't post quantum"); + using AsymmetricX25519PublicKey publicKey = new(keyExchangeData); + return DeriveKey(publicKey as IAsymmetricPublicKey); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + public override byte[] DeriveKey(IAsymmetricPublicKey publicKey) + { + try + { + EnsureUndisposed(); + if (CryptoHelper.StrictPostQuantumSafety) throw new InvalidOperationException($"Post quantum safety-forced - {Algorithm.Name} isn't post quantum"); + if (publicKey is not AsymmetricX25519PublicKey key) throw new ArgumentException($"Public {Algorithm.Name} key required", nameof(publicKey)); + X25519Agreement agreement = new(); + agreement.Init(PrivateKey); + byte[] res = new byte[agreement.AgreementSize]; + try + { + agreement.CalculateAgreement(key.PublicKey, res); + return res; + } + catch + { + res.Clear(); + throw; + } + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override X25519PublicKeyParameters GetPublicKey(X25519PrivateKeyParameters privateKey) => privateKey.GeneratePublicKey(); + + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + } + + /// + protected override async Task DisposeCore() + { + await base.DisposeCore().DynamicContext(); + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + } + + /// + /// Cast to public key + /// + /// Private key + public static implicit operator AsymmetricX25519PublicKey(AsymmetricX25519PrivateKey privateKey) => privateKey.PublicKey; + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricX25519PrivateKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricX25519PublicKey.cs b/src/wan24-Crypto-BC/AsymmetricX25519PublicKey.cs new file mode 100644 index 0000000..f52bb6f --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricX25519PublicKey.cs @@ -0,0 +1,55 @@ +using Org.BouncyCastle.Crypto.Parameters; + +namespace wan24.Crypto.BC +{ + /// + /// X25519 asymmetric public key + /// + public sealed record class AsymmetricX25519PublicKey + : BouncyCastleAsymmetricNonPqcPublicKeyBase + { + /// + /// Constructor + /// + public AsymmetricX25519PublicKey() : base(AsymmetricX25519Algorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricX25519PublicKey(byte[] keyData) : base(AsymmetricX25519Algorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Public key + public AsymmetricX25519PublicKey(X25519PublicKeyParameters publicKey) : base(AsymmetricX25519Algorithm.ALGORITHM_NAME, publicKey) { } + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + return 256; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricX25519PublicKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricX448Algorithm.cs b/src/wan24-Crypto-BC/AsymmetricX448Algorithm.cs new file mode 100644 index 0000000..1caf7b5 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricX448Algorithm.cs @@ -0,0 +1,95 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using System.Collections.ObjectModel; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// X448 asymmetric algorithm + /// + public sealed record class AsymmetricX448Algorithm + : BouncyCastleAsymmetricAlgorithmBase< + AsymmetricX448PublicKey, + AsymmetricX448PrivateKey, + X448KeyPairGenerator, + X448KeyGenerationParameters, + AsymmetricKeyParameter, + X448PublicKeyParameters, + X448PrivateKeyParameters, + AsymmetricX448Algorithm + > + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "X448"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 11; + /// + /// Default key size in bits + /// + public const int DEFAULT_KEY_SIZE = 456; + /// + /// Algorithm usages + /// + public const AsymmetricAlgorithmUsages USAGES = AsymmetricAlgorithmUsages.KeyExchange; + /// + /// Display name + /// + public const string DISPLAY_NAME = "X448"; + + /// + /// Allowed key sizes in bits + /// + private static readonly ReadOnlyCollection _AllowedKeySizes; + + /// + /// Static constructor + /// + static AsymmetricX448Algorithm() => _AllowedKeySizes = new List() + { + 448, + 456 + }.AsReadOnly(); + + /// + /// Constructor + /// + public AsymmetricX448Algorithm() + : base(ALGORITHM_NAME, ALGORITHM_VALUE, USAGES, isEllipticCurveAlgorithm: true, _AllowedKeySizes, isPostQuantum: false, DEFAULT_KEY_SIZE) + { } + + + /// + public override AsymmetricX448PrivateKey CreateKeyPair(CryptoOptions? options = null) + { + try + { + options ??= DefaultOptions; + if (!options.AsymmetricKeyBits.In(AllowedKeySizes)) throw new ArgumentException("Invalid key size", nameof(options)); + X448KeyPairGenerator keyGen = new(); + keyGen.Init(new X448KeyGenerationParameters(new SecureRandom(BouncyCastleRandomGenerator.Instance()))); + return new(keyGen.GenerateKeyPair()); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + protected override AsymmetricKeyParameter GetEngineParameters(CryptoOptions options) => throw new NotSupportedException(); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricX448PrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricX448PrivateKey.cs new file mode 100644 index 0000000..bb0e40e --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricX448PrivateKey.cs @@ -0,0 +1,148 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Parameters; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// X448 asymmetric private key + /// + public sealed record class AsymmetricX448PrivateKey + : BouncyCastleAsymmetricNonPqcPrivateKeyBase< + AsymmetricX448PublicKey, + AsymmetricX448Algorithm, + X448PublicKeyParameters, + X448PrivateKeyParameters, + AsymmetricX448PrivateKey + >, IKeyExchangePrivateKey + { + /// + /// Constructor + /// + public AsymmetricX448PrivateKey() : base(AsymmetricX448Algorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricX448PrivateKey(byte[] keyData) : base(AsymmetricX448Algorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Keys + public AsymmetricX448PrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricX448Algorithm.ALGORITHM_NAME, keys) { } + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + return 456; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + public override (byte[] Key, byte[] KeyExchangeData) GetKeyExchangeData(IAsymmetricPublicKey? publicKey = null, CryptoOptions? options = null) + { + try + { + EnsureUndisposed(); + if (CryptoHelper.StrictPostQuantumSafety) throw new InvalidOperationException($"Post quantum safety-forced - {Algorithm.Name} isn't post quantum"); + publicKey ??= options?.PublicKey ?? options?.PrivateKey?.PublicKey ?? PublicKey; + if (publicKey is not AsymmetricX448PublicKey key) throw new ArgumentException($"Public {Algorithm.Name} key required", nameof(publicKey)); + return (DeriveKey(publicKey), PublicKey.KeyData.Array.CloneArray()); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + public override byte[] DeriveKey(byte[] keyExchangeData) + { + try + { + EnsureUndisposed(); + if (CryptoHelper.StrictPostQuantumSafety) throw new InvalidOperationException($"Post quantum safety-forced - {Algorithm.Name} isn't post quantum"); + using AsymmetricX448PublicKey publicKey = new(keyExchangeData); + return DeriveKey(publicKey as IAsymmetricPublicKey); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + public override byte[] DeriveKey(IAsymmetricPublicKey publicKey) + { + try + { + EnsureUndisposed(); + if (CryptoHelper.StrictPostQuantumSafety) throw new InvalidOperationException($"Post quantum safety-forced - {Algorithm.Name} isn't post quantum"); + if (publicKey is not AsymmetricX448PublicKey key) throw new ArgumentException($"Public {Algorithm.Name} key required", nameof(publicKey)); + X448Agreement agreement = new(); + agreement.Init(PrivateKey); + byte[] res = new byte[agreement.AgreementSize]; + try + { + agreement.CalculateAgreement(key.PublicKey, res); + return res; + } + catch + { + res.Clear(); + throw; + } + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override X448PublicKeyParameters GetPublicKey(X448PrivateKeyParameters privateKey) => privateKey.GeneratePublicKey(); + + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + } + + /// + protected override async Task DisposeCore() + { + await base.DisposeCore().DynamicContext(); + Keys?.Private.ClearPrivateByteArrayFields();//TODO All parameter fields are private :( + } + + /// + /// Cast to public key + /// + /// Private key + public static implicit operator AsymmetricX448PublicKey(AsymmetricX448PrivateKey privateKey) => privateKey.PublicKey; + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricX448PrivateKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricX448PublicKey.cs b/src/wan24-Crypto-BC/AsymmetricX448PublicKey.cs new file mode 100644 index 0000000..62ce559 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricX448PublicKey.cs @@ -0,0 +1,55 @@ +using Org.BouncyCastle.Crypto.Parameters; + +namespace wan24.Crypto.BC +{ + /// + /// X448 asymmetric public key + /// + public sealed record class AsymmetricX448PublicKey + : BouncyCastleAsymmetricNonPqcPublicKeyBase + { + /// + /// Constructor + /// + public AsymmetricX448PublicKey() : base(AsymmetricX448Algorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricX448PublicKey(byte[] keyData) : base(AsymmetricX448Algorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Public key + public AsymmetricX448PublicKey(X448PublicKeyParameters publicKey) : base(AsymmetricX448Algorithm.ALGORITHM_NAME, publicKey) { } + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + return 448; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricX448PublicKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/BcEllipticCurves.cs b/src/wan24-Crypto-BC/BcEllipticCurves.cs new file mode 100644 index 0000000..8607ab9 --- /dev/null +++ b/src/wan24-Crypto-BC/BcEllipticCurves.cs @@ -0,0 +1,50 @@ +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Crypto.Parameters; + +namespace wan24.Crypto.BC +{ + /// + /// Bouncy Castle elliptic curves + /// + public static class BcEllipticCurves + { + /// + /// secp256r1 curve (NIST P-256) + /// + public static readonly ECDomainParameters SECP256R1_CURVE = new(SecNamedCurves.GetByOid(SecObjectIdentifiers.SecP256r1)); + /// + /// secp384r1 curve (NIST P-384) + /// + public static readonly ECDomainParameters SECP384R1_CURVE = new(SecNamedCurves.GetByOid(SecObjectIdentifiers.SecP384r1)); + /// + /// secp521r1 curve (NIST P-521) + /// + public static readonly ECDomainParameters SECP521R1_CURVE = new(SecNamedCurves.GetByOid(SecObjectIdentifiers.SecP521r1)); + + /// + /// Get the key size for a curve + /// + /// Curve name + /// Key size in bits + public static int GetKeySize(ECDomainParameters curve) + { + if (curve.Equals(SECP256R1_CURVE)) return EllipticCurves.SECP256R1_KEY_SIZE; + if (curve.Equals(SECP384R1_CURVE)) return EllipticCurves.SECP384R1_KEY_SIZE; + if (curve.Equals(SECP521R1_CURVE)) return EllipticCurves.SECP521R1_KEY_SIZE; + throw new ArgumentException("Unknown curve", nameof(curve)); + } + + /// + /// Get the curve from a key size + /// + /// Key size in bits + /// Curve name + public static ECDomainParameters GetCurve(int bits) => bits switch + { + EllipticCurves.SECP256R1_KEY_SIZE => SECP256R1_CURVE, + EllipticCurves.SECP384R1_KEY_SIZE => SECP384R1_CURVE, + EllipticCurves.SECP521R1_KEY_SIZE => SECP521R1_CURVE, + _ => throw new ArgumentException("Unknown key size", nameof(bits)) + }; + } +} diff --git a/src/wan24-Crypto-BC/Bootstrap.cs b/src/wan24-Crypto-BC/Bootstrap.cs index 77d05c2..0b7f2e0 100644 --- a/src/wan24-Crypto-BC/Bootstrap.cs +++ b/src/wan24-Crypto-BC/Bootstrap.cs @@ -17,12 +17,16 @@ public static void Boot() // Asymmetric AsymmetricHelper.Algorithms[AsymmetricKyberAlgorithm.ALGORITHM_NAME] = AsymmetricKyberAlgorithm.Instance; //FIXME PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo doesn't support FrodoPrivateKeyParameters !? (waiting for an update of the NuGet package at present) - //AsymmetricHelper.Algorithms[AsymmetricFrodoKemAlgorithm.ALGORITHM_NAME] = AsymmetricFrodoKemAlgorithm.Instance; + AsymmetricHelper.Algorithms[AsymmetricFrodoKemAlgorithm.ALGORITHM_NAME] = AsymmetricFrodoKemAlgorithm.Instance; AsymmetricHelper.Algorithms[AsymmetricDilithiumAlgorithm.ALGORITHM_NAME] = AsymmetricDilithiumAlgorithm.Instance; AsymmetricHelper.Algorithms[AsymmetricFalconAlgorithm.ALGORITHM_NAME] = AsymmetricFalconAlgorithm.Instance; AsymmetricHelper.Algorithms[AsymmetricSphincsPlusAlgorithm.ALGORITHM_NAME] = AsymmetricSphincsPlusAlgorithm.Instance; //FIXME PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo doesn't support NtruPrivateKeyParameters !? (waiting for a fix and an update of the NuGet package at present) - //AsymmetricHelper.Algorithms[AsymmetricNtruEncryptAlgorithm.ALGORITHM_NAME] = AsymmetricNtruEncryptAlgorithm.Instance; + AsymmetricHelper.Algorithms[AsymmetricNtruEncryptAlgorithm.ALGORITHM_NAME] = AsymmetricNtruEncryptAlgorithm.Instance; + AsymmetricHelper.Algorithms[AsymmetricEd25519Algorithm.ALGORITHM_NAME] = AsymmetricEd25519Algorithm.Instance; + AsymmetricHelper.Algorithms[AsymmetricEd448Algorithm.ALGORITHM_NAME] = AsymmetricEd448Algorithm.Instance; + AsymmetricHelper.Algorithms[AsymmetricX25519Algorithm.ALGORITHM_NAME] = AsymmetricX25519Algorithm.Instance; + AsymmetricHelper.Algorithms[AsymmetricX448Algorithm.ALGORITHM_NAME] = AsymmetricX448Algorithm.Instance; // ChaCha20 EncryptionHelper.Algorithms[EncryptionChaCha20Algorithm.ALGORITHM_NAME] = EncryptionChaCha20Algorithm.Instance; CryptoProfiles.Registered[EncryptionChaCha20Algorithm.PROFILE_CHACHA20_RAW] = new CryptoOptions() @@ -72,28 +76,18 @@ public static void Boot() .WithoutCompression() .WithoutMac() .WithEncryptionAlgorithm(EncryptionTwofish256GcmAlgorithm.ALGORITHM_NAME); - // Hash - //TODO .NET 8: SHA3 - HashHelper.Algorithms[HashSha3_256Algorithm.ALGORITHM_NAME] = HashSha3_256Algorithm.Instance; - HashHelper.Algorithms[HashSha3_384Algorithm.ALGORITHM_NAME] = HashSha3_384Algorithm.Instance; - HashHelper.Algorithms[HashSha3_512Algorithm.ALGORITHM_NAME] = HashSha3_512Algorithm.Instance; - // MAC - MacHelper.Algorithms[MacHmacSha3_256Algorithm.ALGORITHM_NAME] = MacHmacSha3_256Algorithm.Instance; - MacHelper.Algorithms[MacHmacSha3_384Algorithm.ALGORITHM_NAME] = MacHmacSha3_384Algorithm.Instance; - MacHelper.Algorithms[MacHmacSha3_512Algorithm.ALGORITHM_NAME] = MacHmacSha3_512Algorithm.Instance; // PQ CryptoHelper.OnForcePostQuantum += (e) => { - //TODO Implement NTRU encryption as new default algorithm for key exchange in v2 if (CryptoHelper.StrictPostQuantumSafety) { if (!AsymmetricHelper.DefaultKeyExchangeAlgorithm.IsPostQuantum) { - AsymmetricHelper.DefaultKeyExchangeAlgorithm = AsymmetricKyberAlgorithm.Instance; + AsymmetricHelper.DefaultKeyExchangeAlgorithm = AsymmetricNtruEncryptAlgorithm.Instance; } else if(!(HybridAlgorithmHelper.KeyExchangeAlgorithm?.IsPostQuantum ?? true)) { - HybridAlgorithmHelper.KeyExchangeAlgorithm = AsymmetricKyberAlgorithm.Instance; + HybridAlgorithmHelper.KeyExchangeAlgorithm = AsymmetricNtruEncryptAlgorithm.Instance; } if (!AsymmetricHelper.DefaultSignatureAlgorithm.IsPostQuantum) { @@ -108,13 +102,13 @@ public static void Boot() { if(AsymmetricHelper.DefaultKeyExchangeAlgorithm.IsPostQuantum && !(HybridAlgorithmHelper.KeyExchangeAlgorithm?.IsPostQuantum ?? false)) { - HybridAlgorithmHelper.KeyExchangeAlgorithm = AsymmetricKyberAlgorithm.Instance; + HybridAlgorithmHelper.KeyExchangeAlgorithm = AsymmetricNtruEncryptAlgorithm.Instance; } else if (!AsymmetricHelper.DefaultKeyExchangeAlgorithm.IsPostQuantum) { if (!(HybridAlgorithmHelper.KeyExchangeAlgorithm?.IsPostQuantum ?? false)) HybridAlgorithmHelper.KeyExchangeAlgorithm = AsymmetricHelper.DefaultKeyExchangeAlgorithm; - AsymmetricHelper.DefaultKeyExchangeAlgorithm = AsymmetricKyberAlgorithm.Instance; + AsymmetricHelper.DefaultKeyExchangeAlgorithm = AsymmetricNtruEncryptAlgorithm.Instance; } if (AsymmetricHelper.DefaultSignatureAlgorithm.IsPostQuantum && !(HybridAlgorithmHelper.SignatureAlgorithm?.IsPostQuantum ?? false)) { diff --git a/src/wan24-Crypto-BC/BouncyCastle.cs b/src/wan24-Crypto-BC/BouncyCastle.cs index 0af5f1b..cfd20fd 100644 --- a/src/wan24-Crypto-BC/BouncyCastle.cs +++ b/src/wan24-Crypto-BC/BouncyCastle.cs @@ -11,23 +11,16 @@ public static class BouncyCastle /// Use the current wan24-Crypto defaults as counter algorithms? public static void SetDefaults(in bool useCurrentDefaultAsCounterAlgorithms = true) { - //TODO In v2 use NTRU as default asymmetric algorithm for key exchange if (useCurrentDefaultAsCounterAlgorithms) { HybridAlgorithmHelper.KeyExchangeAlgorithm = AsymmetricHelper.DefaultKeyExchangeAlgorithm; HybridAlgorithmHelper.SignatureAlgorithm = AsymmetricHelper.DefaultSignatureAlgorithm; - HybridAlgorithmHelper.MacAlgorithm = MacHelper.DefaultAlgorithm; } - AsymmetricHelper.DefaultKeyExchangeAlgorithm = AsymmetricKyberAlgorithm.Instance; + AsymmetricHelper.DefaultKeyExchangeAlgorithm = AsymmetricNtruEncryptAlgorithm.Instance; AsymmetricHelper.DefaultSignatureAlgorithm = AsymmetricDilithiumAlgorithm.Instance; EncryptionHelper.DefaultAlgorithm = EncryptionSerpent256CbcAlgorithm.Instance; - HashHelper.DefaultAlgorithm = HashSha3_512Algorithm.Instance; - MacHelper.DefaultAlgorithm = MacHmacSha3_512Algorithm.Instance; - Pake.DefaultOptions = Pake.DefaultOptions - .WithMac(MacHmacSha3_512Algorithm.Instance.Name, included: false); CryptoOptions pakeCryptoOptions = Pake.DefaultCryptoOptions - .WithEncryptionAlgorithm(EncryptionAes256GcmAlgorithm.Instance.Name); - if (pakeCryptoOptions.MacAlgorithm is not null) pakeCryptoOptions.WithMac(MacHmacSha3_512Algorithm.Instance.Name); + .WithEncryptionAlgorithm(EncryptionSerpent256GcmAlgorithm.ALGORITHM_NAME); Pake.DefaultCryptoOptions = pakeCryptoOptions; } @@ -36,10 +29,69 @@ public static void SetDefaults(in bool useCurrentDefaultAsCounterAlgorithms = tr /// public static void ReplaceNetAlgorithms() { + // Encryption EncryptionHelper.Algorithms[EncryptionAes256CbcAlgorithm.ALGORITHM_NAME] = EncryptionBcAes256CbcAlgorithm.Instance; - if (EncryptionHelper.DefaultAlgorithm.Value == EncryptionAes256CbcAlgorithm.ALGORITHM_VALUE) - EncryptionHelper.DefaultAlgorithm = EncryptionBcAes256CbcAlgorithm.Instance; - //TODO Implement ECDH, ECDSA to replace wan24-Crypto algoritms + switch (EncryptionHelper.DefaultAlgorithm.Value) + { + case EncryptionAes256CbcAlgorithm.ALGORITHM_VALUE: + EncryptionHelper.DefaultAlgorithm = EncryptionBcAes256CbcAlgorithm.Instance; + break; + } + // Hashing + HashHelper.Algorithms[HashBcSha3_256Algorithm.ALGORITHM_NAME] = HashBcSha3_256Algorithm.Instance; + HashHelper.Algorithms[HashBcSha3_384Algorithm.ALGORITHM_NAME] = HashBcSha3_384Algorithm.Instance; + HashHelper.Algorithms[HashBcSha3_512Algorithm.ALGORITHM_NAME] = HashBcSha3_512Algorithm.Instance; + HashHelper.Algorithms[HashBcShake128Algorithm.ALGORITHM_NAME] = HashBcShake128Algorithm.Instance; + HashHelper.Algorithms[HashBcShake256Algorithm.ALGORITHM_NAME] = HashBcShake256Algorithm.Instance; + switch (HashHelper.DefaultAlgorithm.Value) + { + case HashBcSha3_256Algorithm.ALGORITHM_VALUE: + HashHelper.DefaultAlgorithm = HashBcSha3_256Algorithm.Instance; + break; + case HashBcSha3_384Algorithm.ALGORITHM_VALUE: + HashHelper.DefaultAlgorithm = HashBcSha3_384Algorithm.Instance; + break; + case HashBcSha3_512Algorithm.ALGORITHM_VALUE: + HashHelper.DefaultAlgorithm = HashBcSha3_512Algorithm.Instance; + break; + case HashBcShake128Algorithm.ALGORITHM_VALUE: + HashHelper.DefaultAlgorithm = HashBcShake128Algorithm.Instance; + break; + case HashBcShake256Algorithm.ALGORITHM_VALUE: + HashHelper.DefaultAlgorithm = HashBcShake256Algorithm.Instance; + break; + } + // MAC + MacHelper.Algorithms[MacBcHmacSha3_256Algorithm.ALGORITHM_NAME] = MacBcHmacSha3_256Algorithm.Instance; + MacHelper.Algorithms[MacBcHmacSha3_384Algorithm.ALGORITHM_NAME] = MacBcHmacSha3_384Algorithm.Instance; + MacHelper.Algorithms[MacBcHmacSha3_512Algorithm.ALGORITHM_NAME] = MacBcHmacSha3_512Algorithm.Instance; + switch (MacHelper.DefaultAlgorithm.Value) + { + case MacBcHmacSha3_256Algorithm.ALGORITHM_VALUE: + MacHelper.DefaultAlgorithm = MacBcHmacSha3_256Algorithm.Instance; + break; + case MacBcHmacSha3_384Algorithm.ALGORITHM_VALUE: + MacHelper.DefaultAlgorithm = MacBcHmacSha3_384Algorithm.Instance; + break; + case MacBcHmacSha3_512Algorithm.ALGORITHM_VALUE: + MacHelper.DefaultAlgorithm = MacBcHmacSha3_512Algorithm.Instance; + break; + } + // Asymmetric + AsymmetricHelper.Algorithms[AsymmetricBcEcDiffieHellmanAlgorithm.ALGORITHM_NAME] = AsymmetricBcEcDiffieHellmanAlgorithm.Instance; + AsymmetricHelper.Algorithms[AsymmetricBcEcDsaAlgorithm.ALGORITHM_NAME] = AsymmetricBcEcDsaAlgorithm.Instance; + switch (AsymmetricHelper.DefaultSignatureAlgorithm.Value) + { + case AsymmetricBcEcDsaAlgorithm.ALGORITHM_VALUE: + AsymmetricHelper.DefaultSignatureAlgorithm = AsymmetricBcEcDsaAlgorithm.Instance; + break; + } + switch (AsymmetricHelper.DefaultKeyExchangeAlgorithm.Value) + { + case AsymmetricBcEcDiffieHellmanAlgorithm.ALGORITHM_VALUE: + AsymmetricHelper.DefaultKeyExchangeAlgorithm = AsymmetricBcEcDiffieHellmanAlgorithm.Instance; + break; + } } } } diff --git a/src/wan24-Crypto-BC/BouncyCastleAeadCipherAlgorithmBase.cs b/src/wan24-Crypto-BC/BouncyCastleAeadCipherAlgorithmBase.cs index cd5db5b..4b7ffbf 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAeadCipherAlgorithmBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAeadCipherAlgorithmBase.cs @@ -15,7 +15,7 @@ namespace wan24.Crypto.BC /// Constructor /// /// Algorithm name - /// Agorithm value + /// Algorithm value protected BouncyCastleAeadCipherAlgorithmBase(string name, int value) : base(name, value) { } /// diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricAlgorithmBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricAlgorithmBase.cs index de7bd25..faa1b80 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricAlgorithmBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricAlgorithmBase.cs @@ -35,7 +35,7 @@ public abstract record class BouncyCastleAsymmetricAlgorithmBase /// Constructor /// - /// Algorthm name + /// Algorithm name /// Algorithm value /// Algorithm usages /// Is an elliptic curve algorithm? @@ -78,12 +78,11 @@ int defaultKeySize public sealed override bool IsPostQuantum { get; } /// - public sealed override tPrivate CreateKeyPair(CryptoOptions? options = null) + public override tPrivate CreateKeyPair(CryptoOptions? options = null) { try { options ??= DefaultOptions; - options = AsymmetricHelper.GetDefaultKeyExchangeOptions(options); if (!options.AsymmetricKeyBits.In(AllowedKeySizes)) throw new ArgumentException("Invalid key size", nameof(options)); tKeyGen keyGen = new(); keyGen.Init(CreateKeyGenParameters(new SecureRandom(BouncyCastleRandomGenerator.Instance()), GetEngineParameters(options), options)); @@ -100,6 +99,9 @@ public sealed override tPrivate CreateKeyPair(CryptoOptions? options = null) } } + /// + public override tPrivate DeserializePrivateKeyV1(byte[] keyData) => throw new NotSupportedException(); + /// /// Get the cipher engine parameters /// @@ -108,7 +110,7 @@ public sealed override tPrivate CreateKeyPair(CryptoOptions? options = null) protected abstract tParam GetEngineParameters(CryptoOptions options); /// - /// Create key generatpr parameters + /// Create key generator parameters /// /// Random /// Engine parameters diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateKeyBase.cs new file mode 100644 index 0000000..8820b61 --- /dev/null +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateKeyBase.cs @@ -0,0 +1,164 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; +using wan24.Core; +using wan24.StreamSerializerExtensions; + +namespace wan24.Crypto.BC +{ + /// + /// Base class for a Bouncy Castle asymmetric non-PQC private key + /// + /// Public key type + /// Algorithm type + /// Internal public key type + /// Internal private key type + /// Final type + public abstract record class BouncyCastleAsymmetricNonPqcPrivateKeyBase + : BouncyCastleAsymmetricPrivateKeyBase + where tPublic : BouncyCastleAsymmetricNonPqcPublicKeyBase, new() + where tAlgo : IAsymmetricAlgorithm, new() + where tPublicKey : AsymmetricKeyParameter, ICipherParameters + where tPrivateKey : AsymmetricKeyParameter + where tFinal : BouncyCastleAsymmetricNonPqcPrivateKeyBase, new() + { + /// + /// Constructor + /// + /// Algorithm name + protected BouncyCastleAsymmetricNonPqcPrivateKeyBase(string algorithm) : base(algorithm) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Key data + protected BouncyCastleAsymmetricNonPqcPrivateKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Keys + protected BouncyCastleAsymmetricNonPqcPrivateKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + + /// + protected override byte[] SerializeKeyData() + { + try + { + EnsureUndisposed(); + if (Keys == null) throw new InvalidOperationException(); + return PrivateKeyInfoFactory.CreatePrivateKeyInfo(Keys.Private).GetDerEncoded(); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override void DeserializeKeyData() + { + try + { + EnsureUndisposed(); + tPrivateKey? privateKey = null; + tPublicKey? publicKey = null; + try + { + privateKey = (tPrivateKey)PrivateKeyFactory.CreateKey(KeyData.Array); + publicKey = GetPublicKey(privateKey); + Keys = new(publicKey, privateKey); + } + catch + { + privateKey?.ClearPrivateByteArrayFields(); + publicKey?.ClearPrivateByteArrayFields(); + throw; + } + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override byte[] SerializeFullKeyData() + { + try + { + EnsureUndisposed(); + if (Keys == null) throw new InvalidOperationException(); + using MemoryPoolStream ms = new() + { + CleanReturned = true + }; + ms.WriteSerializerVersion() + .WriteBytes(PrivateKeyInfoFactory.CreatePrivateKeyInfo(Keys.Private).GetDerEncoded()) + .WriteBytes(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(Keys.Public).GetDerEncoded()); + return ms.ToArray(); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override void DeserializeFullKeyData() + { + try + { + EnsureUndisposed(); + byte[]? privateKeyInfo = null, + publicKeyInfo = null; + tPrivateKey? privateKey = null; + tPublicKey? publicKey = null; + try + { + using MemoryStream ms = new(KeyData.Array); + int ssv = ms.ReadSerializerVersion(); + privateKeyInfo = ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value; + privateKey = (tPrivateKey)PrivateKeyFactory.CreateKey(privateKeyInfo); + publicKeyInfo = ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value; + publicKey = (tPublicKey)PublicKeyFactory.CreateKey(publicKeyInfo); + Keys = new(publicKey, privateKey); + } + catch + { + privateKey?.ClearPrivateByteArrayFields(); + publicKey?.ClearPrivateByteArrayFields(); + throw; + } + finally + { + privateKeyInfo?.Clear(); + publicKeyInfo?.Clear(); + } + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } +} diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase.cs new file mode 100644 index 0000000..11d8f3b --- /dev/null +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase.cs @@ -0,0 +1,60 @@ +using Org.BouncyCastle.Crypto; + +namespace wan24.Crypto.BC +{ + /// + /// Base class for a Bouncy Castle asymmetric non-PQC private signature key + /// + /// Public key type + /// Algorithm type + /// Internal public key type + /// Internal private key type + /// Signer type + /// Final type + public abstract record class BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase + : BouncyCastleAsymmetricNonPqcPrivateKeyBase, ISignaturePrivateKey + where tPublic : BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase, new() + where tAlgo : IAsymmetricAlgorithm, new() + where tPublicKey : AsymmetricKeyParameter, ICipherParameters + where tPrivateKey : AsymmetricKeyParameter + where tSigner : class, ISigner, new() + where tFinal : BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase, new() + { + /// + /// Constructor + /// + /// Algorithm name + protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase(string algorithm) : base(algorithm) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Key data + protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Keys + protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + + /// + public override byte[] SignHashRaw(byte[] hash) + { + try + { + EnsureUndisposed(); + tSigner signer = new(); + signer.Init(forSigning: true, PrivateKey); + signer.BlockUpdate(hash); + return signer.GenerateSignature(); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } +} diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2.cs new file mode 100644 index 0000000..64817a1 --- /dev/null +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2.cs @@ -0,0 +1,61 @@ +using Org.BouncyCastle.Crypto; + +namespace wan24.Crypto.BC +{ + /// + /// Base class for a Bouncy Castle asymmetric non-PQC private signature key (which requires a context constructor parameter) + /// + /// Public key type + /// Algorithm type + /// Internal public key type + /// Internal private key type + /// Signer type + /// Final type + public abstract record class BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2 + : BouncyCastleAsymmetricNonPqcPrivateKeyBase, ISignaturePrivateKey + where tPublic : BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase2, new() + where tAlgo : IAsymmetricAlgorithm, new() + where tPublicKey : AsymmetricKeyParameter, ICipherParameters + where tPrivateKey : AsymmetricKeyParameter + where tSigner : class, ISigner + where tFinal : BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2, new() + { + /// + /// Constructor + /// + /// Algorithm name + protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2(string algorithm) : base(algorithm) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Key data + protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2(string algorithm, byte[] keyData) : base(algorithm, keyData) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Keys + protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + + /// + public override byte[] SignHashRaw(byte[] hash) + { + try + { + EnsureUndisposed(); + tSigner signer = Activator.CreateInstance(typeof(tSigner), Array.Empty()) as tSigner + ?? throw CryptographicException.From(new InvalidProgramException($"Failed to instance {typeof(tSigner)}")); + signer.Init(forSigning: true, PrivateKey); + signer.BlockUpdate(hash); + return signer.GenerateSignature(); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } +} diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicKeyBase.cs new file mode 100644 index 0000000..e96ec08 --- /dev/null +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicKeyBase.cs @@ -0,0 +1,75 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace wan24.Crypto.BC +{ + /// + /// Base class fo a Bouncy Castle asymmetric non-PQC public key + /// + /// Algorithm type + /// Internal public key type + /// Final type + public abstract record class BouncyCastleAsymmetricNonPqcPublicKeyBase : BouncyCastleAsymmetricPublicKeyBase + where tAlgo : IAsymmetricAlgorithm, new() + where tPublicKey : AsymmetricKeyParameter, ICipherParameters + where tFinal : BouncyCastleAsymmetricNonPqcPublicKeyBase, new() + { + /// + /// Constructor + /// + /// Algorithm name + protected BouncyCastleAsymmetricNonPqcPublicKeyBase(string algorithm) : base(algorithm) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Key data + protected BouncyCastleAsymmetricNonPqcPublicKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Public key + protected BouncyCastleAsymmetricNonPqcPublicKeyBase(string algorithm, tPublicKey publicKey) : base(algorithm, publicKey) { } + + /// + protected override byte[] SerializeKeyData() + { + try + { + EnsureUndisposed(); + if (_PublicKey == null) throw new InvalidOperationException(); + return SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(_PublicKey).GetDerEncoded(); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override void DeserializeKeyData() + { + try + { + EnsureUndisposed(); + _PublicKey = (tPublicKey)PublicKeyFactory.CreateKey(KeyData.Array); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } +} diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase.cs new file mode 100644 index 0000000..e3eecb3 --- /dev/null +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase.cs @@ -0,0 +1,81 @@ +using Org.BouncyCastle.Crypto; + +namespace wan24.Crypto.BC +{ + /// + /// Base class for a Bouncy Castle asymmetric non-PQC public signature key + /// + /// Algorithm type + /// Internal public key type + /// Signer type + /// Final type + public abstract record class BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase + : BouncyCastleAsymmetricNonPqcPublicKeyBase, ISignaturePublicKey + where tAlgo : IAsymmetricAlgorithm, new() + where tPublicKey : AsymmetricKeyParameter, ICipherParameters + where tSigner : class, ISigner, new() + where tFinal : BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase, new() + { + /// + /// Constructor + /// + protected BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase(string algorithm) : base(algorithm) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Key data + protected BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Public key + protected BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase(string algorithm, tPublicKey publicKey) : base(algorithm, publicKey) { } + + /// + public sealed override bool ValidateSignatureRaw(byte[] signature, byte[] signedHash, bool throwOnError = true) + { + try + { + EnsureUndisposed(); + tSigner signer = new(); + signer.Init(forSigning: false, PublicKey); + signer.BlockUpdate(signedHash); + bool res = signer.VerifySignature(signature); + if (!res && throwOnError) throw new InvalidDataException("Signature validation failed"); + return res; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected sealed override bool ValidateSignatureInt(SignatureContainer signature, bool throwOnError = true) + { + try + { + EnsureUndisposed(); + bool res = ValidateSignatureRaw(signature.Signature, signature.CreateSignatureHash()); + if (!res && throwOnError) throw new InvalidDataException("Signature validation failed"); + return res; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } +} diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase2.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase2.cs new file mode 100644 index 0000000..82b953d --- /dev/null +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase2.cs @@ -0,0 +1,82 @@ +using Org.BouncyCastle.Crypto; + +namespace wan24.Crypto.BC +{ + /// + /// Base class for a Bouncy Castle asymmetric non-PQC public signature key (which requires a context constructor parameter) + /// + /// Algorithm type + /// Internal public key type + /// Signer type + /// Final type + public abstract record class BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase2 + : BouncyCastleAsymmetricNonPqcPublicKeyBase, ISignaturePublicKey + where tAlgo : IAsymmetricAlgorithm, new() + where tPublicKey : AsymmetricKeyParameter, ICipherParameters + where tSigner : class, ISigner + where tFinal : BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase2, new() + { + /// + /// Constructor + /// + protected BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase2(string algorithm) : base(algorithm) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Key data + protected BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase2(string algorithm, byte[] keyData) : base(algorithm, keyData) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Public key + protected BouncyCastleAsymmetricNonPqcPublicSignatureKeyBase2(string algorithm, tPublicKey publicKey) : base(algorithm, publicKey) { } + + /// + public sealed override bool ValidateSignatureRaw(byte[] signature, byte[] signedHash, bool throwOnError = true) + { + try + { + EnsureUndisposed(); + tSigner signer = Activator.CreateInstance(typeof(tSigner), Array.Empty()) as tSigner + ?? throw CryptographicException.From(new InvalidProgramException($"Failed to instance {typeof(tSigner)}")); + signer.Init(forSigning: false, PublicKey); + signer.BlockUpdate(signedHash); + bool res = signer.VerifySignature(signature); + if (!res && throwOnError) throw new InvalidDataException("Signature validation failed"); + return res; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected sealed override bool ValidateSignatureInt(SignatureContainer signature, bool throwOnError = true) + { + try + { + EnsureUndisposed(); + bool res = ValidateSignatureRaw(signature.Signature, signature.CreateSignatureHash()); + if (!res && throwOnError) throw new InvalidDataException("Signature validation failed"); + return res; + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } +} diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyBase.cs new file mode 100644 index 0000000..72a825e --- /dev/null +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyBase.cs @@ -0,0 +1,152 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Pqc.Crypto.Utilities; +using wan24.Core; +using wan24.StreamSerializerExtensions; + +namespace wan24.Crypto.BC +{ + /// + /// Base class for a Bouncy Castle asymmetric PQC private key + /// + /// Public key type + /// Algorithm type + /// Internal public key type + /// Internal private key type + /// Final type + public abstract record class BouncyCastleAsymmetricPqcPrivateKeyBase : BouncyCastleAsymmetricPrivateKeyBase + where tPublic : BouncyCastleAsymmetricPqcPublicKeyBase, new() + where tAlgo : IAsymmetricAlgorithm, new() + where tPublicKey : AsymmetricKeyParameter, ICipherParameters + where tPrivateKey: AsymmetricKeyParameter + where tFinal : BouncyCastleAsymmetricPqcPrivateKeyBase, new() + { + /// + /// Constructor + /// + /// Algorithm name + protected BouncyCastleAsymmetricPqcPrivateKeyBase(string algorithm) : base(algorithm) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Key data + protected BouncyCastleAsymmetricPqcPrivateKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Keys + protected BouncyCastleAsymmetricPqcPrivateKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + + /// + protected override byte[] SerializeKeyData() + { + try + { + EnsureUndisposed(); + if (Keys == null) throw new InvalidOperationException(); + return PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo(Keys.Private).GetDerEncoded(); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override void DeserializeKeyData() + { + try + { + EnsureUndisposed(); + tPrivateKey? privateKey = null; + tPublicKey? publicKey = null; + try + { + privateKey = (tPrivateKey)PqcPrivateKeyFactory.CreateKey(KeyData.Array); + publicKey = GetPublicKey(privateKey); + Keys = new(publicKey, privateKey); + } + catch + { + privateKey?.ClearPrivateByteArrayFields(); + publicKey?.ClearPrivateByteArrayFields(); + throw; + } + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override byte[] SerializeFullKeyData() + { + try + { + EnsureUndisposed(); + if (Keys == null) throw new InvalidOperationException(); + using MemoryPoolStream ms = new() + { + CleanReturned = true + }; + ms.WriteSerializerVersion() + .WriteBytes(PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo(Keys.Private).GetDerEncoded()) + .WriteBytes(PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(Keys.Public).GetDerEncoded()); + return ms.ToArray(); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override void DeserializeFullKeyData() + { + try + { + EnsureUndisposed(); + tPrivateKey? privateKey = null; + tPublicKey? publicKey = null; + try + { + using MemoryStream ms = new(KeyData.Array); + int ssv = ms.ReadSerializerVersion(); + privateKey = (tPrivateKey)PqcPrivateKeyFactory.CreateKey(ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); + publicKey = (tPublicKey)PqcPublicKeyFactory.CreateKey(ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); + Keys = new(publicKey, privateKey); + } + catch + { + privateKey?.ClearPrivateByteArrayFields(); + publicKey?.ClearPrivateByteArrayFields(); + throw; + } + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } +} diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateKeyExchangeKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase.cs similarity index 76% rename from src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateKeyExchangeKeyBase.cs rename to src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase.cs index 8fa2569..9d41e13 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateKeyExchangeKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase.cs @@ -1,10 +1,11 @@ using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Security; +using wan24.Core; namespace wan24.Crypto.BC { /// - /// Base class for a Bouncy Castle asymmetric private key exchange key + /// Base class for a Bouncy Castle asymmetric PQC private key exchange key /// /// Public key type /// Algorithm type @@ -13,35 +14,35 @@ namespace wan24.Crypto.BC /// Key generator type /// Key extractor type /// Final type - public abstract record class BouncyCastleAsymmetricPrivateKeyExchangeKeyBase - : BouncyCastleAsymmetricPrivateKeyBase, IKeyExchangePrivateKey - where tPublic : BouncyCastleAsymmetricPublicKeyBase, new() + public abstract record class BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase + : BouncyCastleAsymmetricPqcPrivateKeyBase, IKeyExchangePrivateKey + where tPublic : BouncyCastleAsymmetricPqcPublicKeyBase, new() where tAlgo : IAsymmetricAlgorithm, new() where tPublicKey : AsymmetricKeyParameter, ICipherParameters where tPrivateKey : AsymmetricKeyParameter where tGenerator : class, IEncapsulatedSecretGenerator where tExtractor : class, IEncapsulatedSecretExtractor - where tFinal : BouncyCastleAsymmetricPrivateKeyExchangeKeyBase, new() + where tFinal : BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase, new() { /// /// Constructor /// /// Algorithm name - protected BouncyCastleAsymmetricPrivateKeyExchangeKeyBase(string algorithm) : base(algorithm) { } + protected BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase(string algorithm) : base(algorithm) { } /// /// Constructor /// /// Algorithm name /// Key data - protected BouncyCastleAsymmetricPrivateKeyExchangeKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) { } + protected BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) { } /// /// Constructor /// /// Algorithm name /// Keys - protected BouncyCastleAsymmetricPrivateKeyExchangeKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + protected BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } /// public override (byte[] Key, byte[] KeyExchangeData) GetKeyExchangeData(IAsymmetricPublicKey? publicKey = null, CryptoOptions? options = null) @@ -54,7 +55,7 @@ public override (byte[] Key, byte[] KeyExchangeData) GetKeyExchangeData(IAsymmet tGenerator generator = Activator.CreateInstance(typeof(tGenerator), new SecureRandom(BouncyCastleRandomGenerator.Instance())) as tGenerator ?? throw new InvalidProgramException($"Failed to instance {typeof(tGenerator)}"); using ISecretWithEncapsulation secret = generator.GenerateEncapsulated(key.PublicKey); - return (secret.GetSecret(), secret.GetEncapsulation()); + return (secret.GetSecret().CloneArray(), secret.GetEncapsulation().CloneArray()); } catch (Exception ex) { @@ -88,7 +89,7 @@ public override byte[] DeriveKey(IAsymmetricPublicKey publicKey) tGenerator generator = Activator.CreateInstance(typeof(tGenerator), new SecureRandom(BouncyCastleRandomGenerator.Instance())) as tGenerator ?? throw new InvalidProgramException($"Failed to instance {typeof(tGenerator)}"); using ISecretWithEncapsulation secret = generator.GenerateEncapsulated(key.PublicKey); - return secret.GetSecret(); + return secret.GetSecret().CloneArray(); } catch (Exception ex) { diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateSignatureKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateSignatureKeyBase.cs similarity index 63% rename from src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateSignatureKeyBase.cs rename to src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateSignatureKeyBase.cs index ec5acab..6ffdb1a 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateSignatureKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateSignatureKeyBase.cs @@ -4,7 +4,7 @@ namespace wan24.Crypto.BC { /// - /// Base class for a Bouncy Castle asymmetric private signature key + /// Base class for a Bouncy Castle asymmetric PQC private signature key /// /// Public key type /// Algorithm type @@ -12,34 +12,34 @@ namespace wan24.Crypto.BC /// Internal private key type /// Signer type /// Final type - public abstract record class BouncyCastleAsymmetricPrivateSignatureKeyBase - : BouncyCastleAsymmetricPrivateKeyBase, ISignaturePrivateKey - where tPublic : BouncyCastleAsymmetricPublicSignatureKeyBase, new() + public abstract record class BouncyCastleAsymmetricPqcPrivateSignatureKeyBase + : BouncyCastleAsymmetricPqcPrivateKeyBase, ISignaturePrivateKey + where tPublic : BouncyCastleAsymmetricPqcPublicSignatureKeyBase, new() where tAlgo : IAsymmetricAlgorithm, new() where tPublicKey : AsymmetricKeyParameter, ICipherParameters where tPrivateKey : AsymmetricKeyParameter where tSigner : class, IMessageSigner, new() - where tFinal : BouncyCastleAsymmetricPrivateSignatureKeyBase, new() + where tFinal : BouncyCastleAsymmetricPqcPrivateSignatureKeyBase, new() { /// /// Constructor /// /// Algorithm name - protected BouncyCastleAsymmetricPrivateSignatureKeyBase(string algorithm) : base(algorithm) { } + protected BouncyCastleAsymmetricPqcPrivateSignatureKeyBase(string algorithm) : base(algorithm) { } /// /// Constructor /// /// Algorithm name /// Key data - protected BouncyCastleAsymmetricPrivateSignatureKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) { } + protected BouncyCastleAsymmetricPqcPrivateSignatureKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) { } /// /// Constructor /// /// Algorithm name /// Keys - protected BouncyCastleAsymmetricPrivateSignatureKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + protected BouncyCastleAsymmetricPqcPrivateSignatureKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } /// public sealed override byte[] SignHashRaw(byte[] hash) diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPublicKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPublicKeyBase.cs new file mode 100644 index 0000000..ccdc373 --- /dev/null +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPublicKeyBase.cs @@ -0,0 +1,74 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Pqc.Crypto.Utilities; + +namespace wan24.Crypto.BC +{ + /// + /// Base class fo a Bouncy Castle asymmetric PQC public key + /// + /// Algorithm type + /// Internal public key type + /// Final type + public abstract record class BouncyCastleAsymmetricPqcPublicKeyBase : BouncyCastleAsymmetricPublicKeyBase + where tAlgo : IAsymmetricAlgorithm, new() + where tPublicKey : AsymmetricKeyParameter, ICipherParameters + where tFinal : BouncyCastleAsymmetricPqcPublicKeyBase, new() + { + /// + /// Constructor + /// + /// Algorithm name + protected BouncyCastleAsymmetricPqcPublicKeyBase(string algorithm) : base(algorithm) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Key data + protected BouncyCastleAsymmetricPqcPublicKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) { } + + /// + /// Constructor + /// + /// Algorithm name + /// Public key + protected BouncyCastleAsymmetricPqcPublicKeyBase(string algorithm, tPublicKey publicKey) : base(algorithm, publicKey) { } + + /// + protected override byte[] SerializeKeyData() + { + try + { + EnsureUndisposed(); + if (_PublicKey == null) throw new InvalidOperationException(); + return PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(_PublicKey).GetDerEncoded(); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override void DeserializeKeyData() + { + try + { + EnsureUndisposed(); + _PublicKey = (tPublicKey)PqcPublicKeyFactory.CreateKey(KeyData.Array); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } +} diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPublicSignatureKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPublicSignatureKeyBase.cs similarity index 75% rename from src/wan24-Crypto-BC/BouncyCastleAsymmetricPublicSignatureKeyBase.cs rename to src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPublicSignatureKeyBase.cs index fce7bda..da9cd0b 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPublicSignatureKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPublicSignatureKeyBase.cs @@ -4,37 +4,37 @@ namespace wan24.Crypto.BC { /// - /// Base class for a Bouncy Castle asymmetric public signature key + /// Base class for a Bouncy Castle asymmetric PQC public signature key /// /// Algorithm type /// Internal public key type /// Signer type /// Final type - public abstract record class BouncyCastleAsymmetricPublicSignatureKeyBase - : BouncyCastleAsymmetricPublicKeyBase, ISignaturePublicKey + public abstract record class BouncyCastleAsymmetricPqcPublicSignatureKeyBase + : BouncyCastleAsymmetricPqcPublicKeyBase, ISignaturePublicKey where tAlgo : IAsymmetricAlgorithm, new() where tPublicKey : AsymmetricKeyParameter, ICipherParameters where tSigner : class, IMessageSigner, new() - where tFinal : BouncyCastleAsymmetricPublicSignatureKeyBase, new() + where tFinal : BouncyCastleAsymmetricPqcPublicSignatureKeyBase, new() { /// /// Constructor /// - protected BouncyCastleAsymmetricPublicSignatureKeyBase(string algorithm) : base(algorithm) { } + protected BouncyCastleAsymmetricPqcPublicSignatureKeyBase(string algorithm) : base(algorithm) { } /// /// Constructor /// /// Algorithm name /// Key data - protected BouncyCastleAsymmetricPublicSignatureKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) => KeyData = new(keyData); + protected BouncyCastleAsymmetricPqcPublicSignatureKeyBase(string algorithm, byte[] keyData) : base(algorithm, keyData) { } /// /// Constructor /// /// Algorithm name /// Public key - protected BouncyCastleAsymmetricPublicSignatureKeyBase(string algorithm, tPublicKey publicKey) : base(algorithm, publicKey) { } + protected BouncyCastleAsymmetricPqcPublicSignatureKeyBase(string algorithm, tPublicKey publicKey) : base(algorithm, publicKey) { } /// public sealed override bool ValidateSignatureRaw(byte[] signature, byte[] signedHash, bool throwOnError = true) diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateKeyBase.cs index 2b1b2de..4829a46 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateKeyBase.cs @@ -1,8 +1,4 @@ using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Pqc.Crypto.Utilities; -using Org.BouncyCastle.Security; -using wan24.Core; -using wan24.StreamSerializerExtensions; namespace wan24.Crypto.BC { @@ -18,7 +14,7 @@ public abstract record class BouncyCastleAsymmetricPrivateKeyBase, new() where tAlgo : IAsymmetricAlgorithm, new() where tPublicKey : AsymmetricKeyParameter, ICipherParameters - where tPrivateKey: AsymmetricKeyParameter + where tPrivateKey : AsymmetricKeyParameter where tFinal : BouncyCastleAsymmetricPrivateKeyBase, new() { /// @@ -37,7 +33,11 @@ protected BouncyCastleAsymmetricPrivateKeyBase(string algorithm) : base(algorith /// /// Algorithm name /// Key data - protected BouncyCastleAsymmetricPrivateKeyBase(string algorithm, byte[] keyData) : this(algorithm) => KeyData = new(keyData); + protected BouncyCastleAsymmetricPrivateKeyBase(string algorithm, byte[] keyData) : base(algorithm) + { + KeyData = new(keyData); + DeserializeKeyData(); + } /// /// Constructor @@ -73,7 +73,7 @@ public tPrivateKey PrivateKey try { EnsureUndisposed(); - if (Keys == null) DeserializeKeyData(); + if (Keys is null) DeserializeKeyData(); return (tPrivateKey)Keys!.Private; } catch (CryptographicException) @@ -95,8 +95,8 @@ public sealed override tPublic PublicKey try { EnsureUndisposed(); - if (Keys == null) throw new InvalidOperationException(); - return _PublicKey ??= Activator.CreateInstance(typeof(tPublic), (tPublicKey)Keys.Public) as tPublic + if (Keys is null) throw new InvalidOperationException(); + return _PublicKey ??= Activator.CreateInstance(typeof(tPublic), Keys.Public) as tPublic ?? throw new InvalidProgramException($"Failed to instance {typeof(tPublic)}"); } catch (CryptographicException) @@ -111,95 +111,35 @@ public sealed override tPublic PublicKey } /// - public sealed override int Bits => PublicKey.Bits; + public override int Bits => PublicKey.Bits; /// /// Serialize the key data /// /// Serialized key data - protected byte[] SerializeKeyData() - { - try - { - EnsureUndisposed(); - if (Keys == null) throw new InvalidOperationException(); - using MemoryPoolStream ms = new() - { - CleanReturned = true - }; - ms.WriteNumber(StreamSerializer.VERSION); - byte[] keyInfo = PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo(Keys.Private).PrivateKeyData.GetEncoded(); - try - { - ms.WriteBytes(keyInfo); - keyInfo.Clear(); - keyInfo = PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(Keys.Public).GetEncoded(); - ms.WriteBytes(keyInfo); - keyInfo.Clear(); - } - catch (Exception ex) - { - throw CryptographicException.From(ex); - } - finally - { - keyInfo.Clear(); - } - return ms.ToArray(); - } - catch (CryptographicException) - { - throw; - } - catch (Exception ex) - { - throw CryptographicException.From(ex); - } - } + protected abstract byte[] SerializeKeyData(); /// /// Deserialize the key data /// - protected void DeserializeKeyData() - { - try - { - EnsureUndisposed(); - using MemoryPoolStream ms = new() - { - CleanReturned = true - }; - ms.Write(KeyData.Array); - ms.Position = 0; - int serializerVersion = ms.ReadNumber(); - if (serializerVersion < 1 || serializerVersion > StreamSerializer.VERSION) throw new SerializerException($"Invalid serializer version {serializerVersion}"); - byte[] keyInfo = ms.ReadBytes(serializerVersion, minLen: 1, maxLen: ushort.MaxValue).Value; - try - { - tPrivateKey privateKey = (tPrivateKey)PrivateKeyFactory.CreateKey(keyInfo); - keyInfo.Clear(); - keyInfo = ms.ReadBytes(serializerVersion, minLen: 1, maxLen: ushort.MaxValue).Value; - tPublicKey publicKey = (tPublicKey)PublicKeyFactory.CreateKey(keyInfo); - keyInfo.Clear(); - Keys = new(publicKey, privateKey); - } - catch (Exception ex) - { - throw CryptographicException.From(ex); - } - finally - { - keyInfo.Clear(); - } - } - catch (CryptographicException) - { - throw; - } - catch (Exception ex) - { - throw CryptographicException.From(ex); - } - } + protected abstract void DeserializeKeyData(); + + /// + /// Serialize the full key data (private and public key) + /// + /// Serialized key data + protected abstract byte[] SerializeFullKeyData(); + + /// + /// Deserialize the full key data (private and public key) + /// + protected abstract void DeserializeFullKeyData(); + + /// + /// Get the public key from a private key + /// + /// Private key + /// Public key + protected abstract tPublicKey GetPublicKey(tPrivateKey privateKey); } } diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPublicKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPublicKeyBase.cs index 0afc911..d67e68a 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPublicKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPublicKeyBase.cs @@ -1,7 +1,4 @@ using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Pqc.Crypto.Utilities; -using wan24.Core; -using wan24.StreamSerializerExtensions; namespace wan24.Crypto.BC { @@ -103,71 +100,11 @@ public sealed override IAsymmetricPublicKey GetCopy() /// Serialize the key data /// /// Serialized key data - protected byte[] SerializeKeyData() - { - try - { - EnsureUndisposed(); - using MemoryStream ms = new(); - byte[] keyInfo = PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(_PublicKey).GetEncoded(); - try - { - ms.WriteNumber(StreamSerializer.VERSION); - ms.WriteBytes(PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(_PublicKey).GetEncoded()); - } - catch (Exception ex) - { - throw CryptographicException.From(ex); - } - finally - { - keyInfo.Clear(); - } - return ms.ToArray(); - } - catch (CryptographicException) - { - throw; - } - catch (Exception ex) - { - throw CryptographicException.From(ex); - } - } + protected abstract byte[] SerializeKeyData(); /// /// Deserialize the key data /// - protected void DeserializeKeyData() - { - try - { - EnsureUndisposed(); - using MemoryStream ms = new(KeyData.Array); - int serializerVersion = ms.ReadNumber(); - if (serializerVersion < 1 || serializerVersion > StreamSerializer.VERSION) throw new SerializerException($"Invalid serializer version {serializerVersion}"); - byte[] keyInfo = ms.ReadBytes(serializerVersion, minLen: 1, maxLen: ushort.MaxValue).Value; - try - { - _PublicKey = (tPublicKey)PqcPublicKeyFactory.CreateKey(keyInfo); - } - catch (Exception ex) - { - throw CryptographicException.From(ex); - } - finally - { - keyInfo.Clear(); - } - } - catch (CryptographicException) - { - throw; - } - catch (Exception ex) - { - throw CryptographicException.From(ex); - } - } + protected abstract void DeserializeKeyData(); } } diff --git a/src/wan24-Crypto-BC/BouncyCastleBlockCipherAlgorithmBase.cs b/src/wan24-Crypto-BC/BouncyCastleBlockCipherAlgorithmBase.cs index 9ee4379..d814917 100644 --- a/src/wan24-Crypto-BC/BouncyCastleBlockCipherAlgorithmBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleBlockCipherAlgorithmBase.cs @@ -20,7 +20,7 @@ namespace wan24.Crypto.BC /// Constructor /// /// Algorithm name - /// Agorithm value + /// Algorithm value protected BouncyCastleBlockCipherAlgorithmBase(string name, int value) : base(name, value) { } /// @@ -34,9 +34,9 @@ protected override byte[] GetValidLengthKey(byte[] key, int len) { HashMd5Algorithm.HASH_LENGTH => HashMd5Algorithm.Instance.Hash(key), HashSha1Algorithm.HASH_LENGTH => HashSha1Algorithm.Instance.Hash(key), - HashSha3_256Algorithm.HASH_LENGTH => HashSha3_256Algorithm.Instance.Hash(key), - HashSha3_384Algorithm.HASH_LENGTH => HashSha3_384Algorithm.Instance.Hash(key), - HashSha3_512Algorithm.HASH_LENGTH => HashSha3_512Algorithm.Instance.Hash(key), + HashBcSha3_256Algorithm.HASH_LENGTH => HashBcSha3_256Algorithm.Instance.Hash(key), + HashBcSha3_384Algorithm.HASH_LENGTH => HashBcSha3_384Algorithm.Instance.Hash(key), + HashBcSha3_512Algorithm.HASH_LENGTH => HashBcSha3_512Algorithm.Instance.Hash(key), _ => throw CryptographicException.From($"Can't process for desired key length {len} bytes", new NotSupportedException()) }; diff --git a/src/wan24-Crypto-BC/BouncyCastleBufferedCipherAlgorithmBase.cs b/src/wan24-Crypto-BC/BouncyCastleBufferedCipherAlgorithmBase.cs index bb3cb2f..707043a 100644 --- a/src/wan24-Crypto-BC/BouncyCastleBufferedCipherAlgorithmBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleBufferedCipherAlgorithmBase.cs @@ -14,7 +14,7 @@ namespace wan24.Crypto.BC /// Constructor /// /// Algorithm name - /// Agorithm value + /// Algorithm value protected BouncyCastleBufferedCipherAlgorithmBase(string name, int value) : base(name, value) { } /// diff --git a/src/wan24-Crypto-BC/BouncyCastleCipherAlgorithmBase.cs b/src/wan24-Crypto-BC/BouncyCastleCipherAlgorithmBase.cs index f3a8d37..f85136b 100644 --- a/src/wan24-Crypto-BC/BouncyCastleCipherAlgorithmBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleCipherAlgorithmBase.cs @@ -20,7 +20,7 @@ namespace wan24.Crypto.BC /// Constructor /// /// Algorithm name - /// Agorithm value + /// Algorithm value protected BouncyCastleCipherAlgorithmBase(string name, int value) : base(name, value) { } /// @@ -34,9 +34,9 @@ protected override byte[] GetValidLengthKey(byte[] key, int len) { HashMd5Algorithm.HASH_LENGTH => HashMd5Algorithm.Instance.Hash(key), HashSha1Algorithm.HASH_LENGTH => HashSha1Algorithm.Instance.Hash(key), - HashSha3_256Algorithm.HASH_LENGTH => HashSha3_256Algorithm.Instance.Hash(key), - HashSha3_384Algorithm.HASH_LENGTH => HashSha3_384Algorithm.Instance.Hash(key), - HashSha3_512Algorithm.HASH_LENGTH => HashSha3_512Algorithm.Instance.Hash(key), + HashBcSha3_256Algorithm.HASH_LENGTH => HashBcSha3_256Algorithm.Instance.Hash(key), + HashBcSha3_384Algorithm.HASH_LENGTH => HashBcSha3_384Algorithm.Instance.Hash(key), + HashBcSha3_512Algorithm.HASH_LENGTH => HashBcSha3_512Algorithm.Instance.Hash(key), _ => throw CryptographicException.From($"Can't process for desired key length {len} bytes", new NotSupportedException()) }; diff --git a/src/wan24-Crypto-BC/BouncyCastleCryptoTransform.cs b/src/wan24-Crypto-BC/BouncyCastleCryptoTransform.cs index e7a22af..2b2ed36 100644 --- a/src/wan24-Crypto-BC/BouncyCastleCryptoTransform.cs +++ b/src/wan24-Crypto-BC/BouncyCastleCryptoTransform.cs @@ -130,35 +130,35 @@ public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int input { if (StreamCipher is not null) { - if (inputCount == 0) return Array.Empty(); + if (inputCount == 0) return []; using RentedArrayRefStruct outputBuffer = new(inputCount, clean: false) { Clear = true }; int used = TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer.Array, 0); - return used == 0 ? Array.Empty() : outputBuffer.Span[..used].ToArray(); + return used == 0 ? [] : outputBuffer.Span[..used].ToArray(); } if (BlockCipher is not null) { - if (inputCount == 0) return Array.Empty(); + if (inputCount == 0) return []; using RentedArrayRefStruct outputBuffer = new(Math.Max(inputCount, OutputBlockSize), clean: false) { Clear = true }; int used = TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer.Array, 0); - return used == 0 ? Array.Empty() : outputBuffer.Span[..used].ToArray(); + return used == 0 ? [] : outputBuffer.Span[..used].ToArray(); } if (BufferedCipher is not null) return BufferedCipher.DoFinal(inputBuffer, inputOffset, inputCount); if (Digest is not null) { - TransformBlock(inputBuffer, inputOffset, inputCount, Array.Empty(), 0); + TransformBlock(inputBuffer, inputOffset, inputCount, [], 0); byte[] res = new byte[OutputBlockSize]; Digest.DoFinal(res); return res; } if (Mac is not null) { - TransformBlock(inputBuffer, inputOffset, inputCount, Array.Empty(), 0); + TransformBlock(inputBuffer, inputOffset, inputCount, [], 0); byte[] res = new byte[OutputBlockSize]; Mac.DoFinal(res); return res; diff --git a/src/wan24-Crypto-BC/BouncyCastleHashAlgorithm.cs b/src/wan24-Crypto-BC/BouncyCastleHashAlgorithm.cs index d194a66..e32c045 100644 --- a/src/wan24-Crypto-BC/BouncyCastleHashAlgorithm.cs +++ b/src/wan24-Crypto-BC/BouncyCastleHashAlgorithm.cs @@ -6,18 +6,19 @@ namespace wan24.Crypto.BC /// /// Bouncy Castle hash algorithm /// - public sealed class BouncyCastleHashAlgorithm : HashAlgorithm + /// + /// Constructor + /// + /// Digest + public sealed class BouncyCastleHashAlgorithm(IDigest digest) : HashAlgorithm() { /// /// Digest /// - private readonly IDigest Digest; + private readonly IDigest Digest = digest; - /// - /// Constructor - /// - /// Digest - public BouncyCastleHashAlgorithm(IDigest digest) : base() => Digest = digest; + /// + public override int HashSize => Digest.GetDigestSize() << 3; /// public override void Initialize() => Digest.Reset(); diff --git a/src/wan24-Crypto-BC/BouncyCastleHmacAlgorithm.cs b/src/wan24-Crypto-BC/BouncyCastleHmacAlgorithm.cs index 87be8f1..70f9afd 100644 --- a/src/wan24-Crypto-BC/BouncyCastleHmacAlgorithm.cs +++ b/src/wan24-Crypto-BC/BouncyCastleHmacAlgorithm.cs @@ -6,18 +6,16 @@ namespace wan24.Crypto.BC /// /// Bouncy Castle HMAC algorithm /// - public sealed class BouncyCastleHmacAlgorithm : KeyedHashAlgorithm + /// + /// Constructor + /// + /// MAC + public sealed class BouncyCastleHmacAlgorithm(IMac mac) : KeyedHashAlgorithm() { /// /// MAC /// - private readonly IMac Mac; - - /// - /// Constructor - /// - /// MAC - public BouncyCastleHmacAlgorithm(IMac mac) : base() => Mac = mac; + private readonly IMac Mac = mac; /// public override void Initialize() => Mac.Reset(); diff --git a/src/wan24-Crypto-BC/BouncyCastleRngWrapper.cs b/src/wan24-Crypto-BC/BouncyCastleRngWrapper.cs index 57d4611..e343d25 100644 --- a/src/wan24-Crypto-BC/BouncyCastleRngWrapper.cs +++ b/src/wan24-Crypto-BC/BouncyCastleRngWrapper.cs @@ -5,18 +5,16 @@ namespace wan24.Crypto.BC /// /// Bouncy Castle RNG wrapper for wan24-Crypto /// - public sealed class BouncyCastleRngWrapper : IBouncyCastleRng + /// + /// Constructor + /// + /// RNG + public sealed class BouncyCastleRngWrapper(in IRandomGenerator rng) : IBouncyCastleRng { - /// - /// Constructor - /// - /// RNG - public BouncyCastleRngWrapper(in IRandomGenerator rng) => RNG = rng; - /// /// Wrapped RNG /// - public IRandomGenerator RNG { get; } + public IRandomGenerator RNG { get; } = rng; /// public void AddSeed(ReadOnlySpan seed) => AddSeedMaterial(seed); @@ -54,7 +52,7 @@ public Task> FillBytesAsync(Memory buffer, CancellationToken /// public byte[] GetBytes(in int count) { - if (count < 1) return Array.Empty(); + if (count < 1) return []; byte[] res = new byte[count]; NextBytes(res); return res; diff --git a/src/wan24-Crypto-BC/BouncyCastleStreamCipherAlgorithmBase.cs b/src/wan24-Crypto-BC/BouncyCastleStreamCipherAlgorithmBase.cs index 2bddf86..5cef109 100644 --- a/src/wan24-Crypto-BC/BouncyCastleStreamCipherAlgorithmBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleStreamCipherAlgorithmBase.cs @@ -20,7 +20,7 @@ namespace wan24.Crypto.BC /// Constructor /// /// Algorithm name - /// Agorithm value + /// Algorithm value protected BouncyCastleStreamCipherAlgorithmBase(string name, int value) : base(name, value) { } /// diff --git a/src/wan24-Crypto-BC/ChaCha20Rng.cs b/src/wan24-Crypto-BC/ChaCha20Rng.cs index 1eab24d..f1af83f 100644 --- a/src/wan24-Crypto-BC/ChaCha20Rng.cs +++ b/src/wan24-Crypto-BC/ChaCha20Rng.cs @@ -3,16 +3,14 @@ /// /// ChaCha20 CSRNG /// - public sealed class ChaCha20Rng : StreamCipherRng + /// + /// Constructor + /// + /// Internal RNG to use (will be disposed, if possible!) + /// Buffer size in bytes (min. ) + /// Seed the given RNG with N byte from (skipped, if or <1) + public sealed class ChaCha20Rng(in ISeedableRng? rng = null, in int? bufferSize = null, in int? seedLength = 256) + : StreamCipherRng(EncryptionChaCha20Algorithm.Instance, rng, bufferSize, seedLength) { - /// - /// Constructor - /// - /// Internal RNG to use (will be disposed, if possible!) - /// Buffer size in bytes (min. ) - /// Seed the given RNG with N byte from (skipped, if or <1) - public ChaCha20Rng(in ISeedableRng? rng = null, in int? bufferSize = null, in int? seedLength = 256) - : base(EncryptionChaCha20Algorithm.Instance, rng, bufferSize, seedLength) - { } } } diff --git a/src/wan24-Crypto-BC/DisposableRngWrapper.cs b/src/wan24-Crypto-BC/DisposableRngWrapper.cs index f29d8c1..726d44a 100644 --- a/src/wan24-Crypto-BC/DisposableRngWrapper.cs +++ b/src/wan24-Crypto-BC/DisposableRngWrapper.cs @@ -6,18 +6,16 @@ namespace wan24.Crypto.BC /// /// Bouncy Castle disposable RNG wrapper for wan24-Crypto /// - public sealed class DisposableRngWrapper : DisposableSeedableRngBase, IRandomGenerator + /// + /// Constructor + /// + /// RNG + public sealed class DisposableRngWrapper(IRandomGenerator rng) : DisposableSeedableRngBase(), IRandomGenerator { - /// - /// Constructor - /// - /// RNG - public DisposableRngWrapper(IRandomGenerator rng) : base() => RNG = rng; - /// /// Wrapped RNG /// - public IRandomGenerator RNG { get; } + public IRandomGenerator RNG { get; } = rng; /// public override void AddSeed(ReadOnlySpan seed) => AddSeedMaterial(seed); diff --git a/src/wan24-Crypto-BC/EncryptionSerpent256CbcAlgorithm.cs b/src/wan24-Crypto-BC/EncryptionSerpent256CbcAlgorithm.cs index 2cbeae6..8a92bf0 100644 --- a/src/wan24-Crypto-BC/EncryptionSerpent256CbcAlgorithm.cs +++ b/src/wan24-Crypto-BC/EncryptionSerpent256CbcAlgorithm.cs @@ -73,7 +73,7 @@ protected override IBufferedCipher CreateCipher(bool forEncryption, CryptoOption => new PaddedBufferedBlockCipher(new CbcBlockCipher(CreateSerpent(options)), new ISO10126d2Padding()); /// - protected override ICipherParameters CreateParameters(byte[] iv, CryptoOptions options) => CreateKeyParameters(iv, options, HashSha3_256Algorithm.Instance); + protected override ICipherParameters CreateParameters(byte[] iv, CryptoOptions options) => CreateKeyParameters(iv, options, HashBcSha3_256Algorithm.Instance); /// /// Create the Serpent engine diff --git a/src/wan24-Crypto-BC/EncryptionTwofish256CbcAlgorithm.cs b/src/wan24-Crypto-BC/EncryptionTwofish256CbcAlgorithm.cs index fabef38..37879e3 100644 --- a/src/wan24-Crypto-BC/EncryptionTwofish256CbcAlgorithm.cs +++ b/src/wan24-Crypto-BC/EncryptionTwofish256CbcAlgorithm.cs @@ -73,7 +73,7 @@ protected override IBufferedCipher CreateCipher(bool forEncryption, CryptoOption => new PaddedBufferedBlockCipher(new CbcBlockCipher(CreateTwofish(options)), new ISO10126d2Padding()); /// - protected override ICipherParameters CreateParameters(byte[] iv, CryptoOptions options) => CreateKeyParameters(iv, options, HashSha3_256Algorithm.Instance); + protected override ICipherParameters CreateParameters(byte[] iv, CryptoOptions options) => CreateKeyParameters(iv, options, HashBcSha3_256Algorithm.Instance); /// /// Create the Twofish engine diff --git a/src/wan24-Crypto-BC/HashSha3_256Algorithm.cs b/src/wan24-Crypto-BC/HashBcSha3_256Algorithm.cs similarity index 74% rename from src/wan24-Crypto-BC/HashSha3_256Algorithm.cs rename to src/wan24-Crypto-BC/HashBcSha3_256Algorithm.cs index 28d4914..ebfa799 100644 --- a/src/wan24-Crypto-BC/HashSha3_256Algorithm.cs +++ b/src/wan24-Crypto-BC/HashBcSha3_256Algorithm.cs @@ -4,9 +4,9 @@ namespace wan24.Crypto.BC { /// - /// SHA3-256 hash algorithm + /// SHA3-256 hash algorithm (may be used as replacement, if the .NET algorithm isn't available on the current platform) /// - public sealed record class HashSha3_256Algorithm : BouncyCastleHashAlgorithmBase + public sealed record class HashBcSha3_256Algorithm : BouncyCastleHashAlgorithmBase { /// /// Algorithm name @@ -28,13 +28,13 @@ public sealed record class HashSha3_256Algorithm : BouncyCastleHashAlgorithmBase /// /// Constructor /// - public HashSha3_256Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } + public HashBcSha3_256Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } /// public override int HashLength => HASH_LENGTH; /// - public override bool IsPostQuantum => false; + public override bool IsPostQuantum => true; /// public override string DisplayName => DISPLAY_NAME; diff --git a/src/wan24-Crypto-BC/HashSha3_384Algorithm.cs b/src/wan24-Crypto-BC/HashBcSha3_384Algorithm.cs similarity index 78% rename from src/wan24-Crypto-BC/HashSha3_384Algorithm.cs rename to src/wan24-Crypto-BC/HashBcSha3_384Algorithm.cs index cedfe09..e58ae74 100644 --- a/src/wan24-Crypto-BC/HashSha3_384Algorithm.cs +++ b/src/wan24-Crypto-BC/HashBcSha3_384Algorithm.cs @@ -4,9 +4,9 @@ namespace wan24.Crypto.BC { /// - /// SHA3-384 hash algorithm + /// SHA3-384 hash algorithm (may be used as replacement, if the .NET algorithm isn't available on the current platform) /// - public sealed record class HashSha3_384Algorithm : BouncyCastleHashAlgorithmBase + public sealed record class HashBcSha3_384Algorithm : BouncyCastleHashAlgorithmBase { /// /// Algorithm name @@ -28,7 +28,7 @@ public sealed record class HashSha3_384Algorithm : BouncyCastleHashAlgorithmBase /// /// Constructor /// - public HashSha3_384Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } + public HashBcSha3_384Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } /// public override int HashLength => HASH_LENGTH; diff --git a/src/wan24-Crypto-BC/HashSha3_512Algorithm.cs b/src/wan24-Crypto-BC/HashBcSha3_512Algorithm.cs similarity index 78% rename from src/wan24-Crypto-BC/HashSha3_512Algorithm.cs rename to src/wan24-Crypto-BC/HashBcSha3_512Algorithm.cs index 4790654..efe05e9 100644 --- a/src/wan24-Crypto-BC/HashSha3_512Algorithm.cs +++ b/src/wan24-Crypto-BC/HashBcSha3_512Algorithm.cs @@ -4,9 +4,9 @@ namespace wan24.Crypto.BC { /// - /// SHA3-512 hash algorithm + /// SHA3-512 hash algorithm (may be used as replacement, if the .NET algorithm isn't available on the current platform) /// - public sealed record class HashSha3_512Algorithm : BouncyCastleHashAlgorithmBase + public sealed record class HashBcSha3_512Algorithm : BouncyCastleHashAlgorithmBase { /// /// Algorithm name @@ -28,7 +28,7 @@ public sealed record class HashSha3_512Algorithm : BouncyCastleHashAlgorithmBase /// /// Constructor /// - public HashSha3_512Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } + public HashBcSha3_512Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } /// public override int HashLength => HASH_LENGTH; diff --git a/src/wan24-Crypto-BC/HashBcShake128Algorithm.cs b/src/wan24-Crypto-BC/HashBcShake128Algorithm.cs new file mode 100644 index 0000000..c2d4150 --- /dev/null +++ b/src/wan24-Crypto-BC/HashBcShake128Algorithm.cs @@ -0,0 +1,45 @@ +using Org.BouncyCastle.Crypto.Digests; +using System.Security.Cryptography; + +namespace wan24.Crypto.BC +{ + /// + /// Shake128 hash algorithm (may be used as replacement, if the .NET algorithm isn't available on the current platform) + /// + public sealed record class HashBcShake128Algorithm : BouncyCastleHashAlgorithmBase + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "SHAKE128"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 8; + /// + /// Hash length in bytes + /// + public const int HASH_LENGTH = 32; + /// + /// Display name + /// + public const string DISPLAY_NAME = "Shake128"; + + /// + /// Constructor + /// + public HashBcShake128Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } + + /// + public override int HashLength => HASH_LENGTH; + + /// + public override bool IsPostQuantum => true; + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + protected override HashAlgorithm GetHashAlgorithmInt(CryptoOptions? options) => new BouncyCastleHashAlgorithm(new ShakeDigest(128)); + } +} diff --git a/src/wan24-Crypto-BC/HashBcShake256Algorithm.cs b/src/wan24-Crypto-BC/HashBcShake256Algorithm.cs new file mode 100644 index 0000000..325d778 --- /dev/null +++ b/src/wan24-Crypto-BC/HashBcShake256Algorithm.cs @@ -0,0 +1,45 @@ +using Org.BouncyCastle.Crypto.Digests; +using System.Security.Cryptography; + +namespace wan24.Crypto.BC +{ + /// + /// Shake256 hash algorithm (may be used as replacement, if the .NET algorithm isn't available on the current platform) + /// + public sealed record class HashBcShake256Algorithm : BouncyCastleHashAlgorithmBase + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "SHAKE256"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 9; + /// + /// Hash length in bytes + /// + public const int HASH_LENGTH = 64; + /// + /// Display name + /// + public const string DISPLAY_NAME = "Shake256"; + + /// + /// Constructor + /// + public HashBcShake256Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } + + /// + public override int HashLength => HASH_LENGTH; + + /// + public override bool IsPostQuantum => true; + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + protected override HashAlgorithm GetHashAlgorithmInt(CryptoOptions? options) => new BouncyCastleHashAlgorithm(new ShakeDigest(256)); + } +} diff --git a/src/wan24-Crypto-BC/InternalReflectionExtensions.cs b/src/wan24-Crypto-BC/InternalReflectionExtensions.cs index 13c21b0..008ca46 100644 --- a/src/wan24-Crypto-BC/InternalReflectionExtensions.cs +++ b/src/wan24-Crypto-BC/InternalReflectionExtensions.cs @@ -10,9 +10,9 @@ namespace wan24.Crypto.BC internal static class InternalReflectionExtensions { /// - /// Private byte array fields (key is the type) + /// Private byte array fields (key is the type hash code) /// - private static readonly ConcurrentDictionary Fields = new(); + private static readonly ConcurrentDictionary Fields = new(); /// /// Clear all private byte array fields @@ -21,12 +21,14 @@ internal static class InternalReflectionExtensions internal static void ClearPrivateByteArrayFields(this object obj) { Type type = obj.GetType(); - if (!Fields.TryGetValue(type, out FieldInfo[]? fields)) + int hashCode = type.GetHashCode(); + if (!Fields.TryGetValue(hashCode, out FieldInfo[]? fields)) { + Type baType = typeof(byte[]); fields = (from fi in type.GetFieldsCached(BindingFlags.Instance | BindingFlags.NonPublic) - where fi.FieldType == typeof(byte[]) + where fi.FieldType == baType select fi).ToArray(); - Fields.TryAdd(type, fields); + Fields.TryAdd(hashCode, fields); } for (int i = 0, len = fields.Length; i < len; (fields[i].GetValue(obj) as byte[])?.Clear(), i++) ; } diff --git a/src/wan24-Crypto-BC/MacHmacSha3_256Algorithm.cs b/src/wan24-Crypto-BC/MacBcHmacSha3_256Algorithm.cs similarity index 71% rename from src/wan24-Crypto-BC/MacHmacSha3_256Algorithm.cs rename to src/wan24-Crypto-BC/MacBcHmacSha3_256Algorithm.cs index 8c2b7db..0dd13e7 100644 --- a/src/wan24-Crypto-BC/MacHmacSha3_256Algorithm.cs +++ b/src/wan24-Crypto-BC/MacBcHmacSha3_256Algorithm.cs @@ -1,5 +1,4 @@ -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; using System.Security.Cryptography; @@ -7,9 +6,9 @@ namespace wan24.Crypto.BC { /// - /// HMAC-SHA3-256 MAC algorithm + /// HMAC-SHA3-256 MAC algorithm (may be used as replacement, if the .NET algorithm isn't available on the current platform) /// - public sealed record class MacHmacSha3_256Algorithm : BouncyCastleHmacAlgorithmBase + public sealed record class MacBcHmacSha3_256Algorithm : BouncyCastleHmacAlgorithmBase { /// /// Algorithm name @@ -31,13 +30,13 @@ public sealed record class MacHmacSha3_256Algorithm : BouncyCastleHmacAlgorithmB /// /// Constructor /// - public MacHmacSha3_256Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } + public MacBcHmacSha3_256Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } /// public override int MacLength => MAC_LENGTH; /// - public override bool IsPostQuantum => false; + public override bool IsPostQuantum => true; /// public override string DisplayName => DISPLAY_NAME; @@ -45,7 +44,7 @@ public MacHmacSha3_256Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } /// protected override KeyedHashAlgorithm GetMacAlgorithmInt(byte[] pwd, CryptoOptions? options) { - IMac mac = new HMac(new Sha3Digest(MAC_LENGTH << 3)); + HMac mac = new(new Sha3Digest(MAC_LENGTH << 3)); mac.Init(new KeyParameter(pwd)); return new BouncyCastleHmacAlgorithm(mac); } diff --git a/src/wan24-Crypto-BC/MacHmacSha3_384Algorithm.cs b/src/wan24-Crypto-BC/MacBcHmacSha3_384Algorithm.cs similarity index 74% rename from src/wan24-Crypto-BC/MacHmacSha3_384Algorithm.cs rename to src/wan24-Crypto-BC/MacBcHmacSha3_384Algorithm.cs index 168f47c..609108a 100644 --- a/src/wan24-Crypto-BC/MacHmacSha3_384Algorithm.cs +++ b/src/wan24-Crypto-BC/MacBcHmacSha3_384Algorithm.cs @@ -1,5 +1,4 @@ -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; using System.Security.Cryptography; @@ -7,9 +6,9 @@ namespace wan24.Crypto.BC { /// - /// HMAC-SHA3-384 MAC algorithm + /// HMAC-SHA3-384 MAC algorithm (may be used as replacement, if the .NET algorithm isn't available on the current platform) /// - public sealed record class MacHmacSha3_384Algorithm : BouncyCastleHmacAlgorithmBase + public sealed record class MacBcHmacSha3_384Algorithm : BouncyCastleHmacAlgorithmBase { /// /// Algorithm name @@ -31,7 +30,7 @@ public sealed record class MacHmacSha3_384Algorithm : BouncyCastleHmacAlgorithmB /// /// Constructor /// - public MacHmacSha3_384Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } + public MacBcHmacSha3_384Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } /// public override int MacLength => MAC_LENGTH; @@ -45,7 +44,7 @@ public MacHmacSha3_384Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } /// protected override KeyedHashAlgorithm GetMacAlgorithmInt(byte[] pwd, CryptoOptions? options) { - IMac mac = new HMac(new Sha3Digest(MAC_LENGTH << 3)); + HMac mac = new(new Sha3Digest(MAC_LENGTH << 3)); mac.Init(new KeyParameter(pwd)); return new BouncyCastleHmacAlgorithm(mac); } diff --git a/src/wan24-Crypto-BC/MacHmacSha3_512Algorithm.cs b/src/wan24-Crypto-BC/MacBcHmacSha3_512Algorithm.cs similarity index 69% rename from src/wan24-Crypto-BC/MacHmacSha3_512Algorithm.cs rename to src/wan24-Crypto-BC/MacBcHmacSha3_512Algorithm.cs index 08ef721..785f41c 100644 --- a/src/wan24-Crypto-BC/MacHmacSha3_512Algorithm.cs +++ b/src/wan24-Crypto-BC/MacBcHmacSha3_512Algorithm.cs @@ -1,5 +1,4 @@ -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; using System.Security.Cryptography; @@ -7,9 +6,9 @@ namespace wan24.Crypto.BC { /// - /// HMAC-SHA3-512 MAC algorithm + /// HMAC-SHA3-512 MAC algorithm (may be used as replacement, if the .NET algorithm isn't available on the current platform) /// - public sealed record class MacHmacSha3_512Algorithm : BouncyCastleHmacAlgorithmBase + public sealed record class MacBcHmacSha3_512Algorithm : BouncyCastleHmacAlgorithmBase { /// /// Algorithm name @@ -18,11 +17,11 @@ public sealed record class MacHmacSha3_512Algorithm : BouncyCastleHmacAlgorithmB /// /// Algorithm value /// - public const int ALGORITHM_VALUE = 5; + public const int ALGORITHM_VALUE = 6; /// /// MAC length in bytes /// - public const int MAC_LENGTH = 48; + public const int MAC_LENGTH = 64; /// /// Display name /// @@ -31,7 +30,7 @@ public sealed record class MacHmacSha3_512Algorithm : BouncyCastleHmacAlgorithmB /// /// Constructor /// - public MacHmacSha3_512Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } + public MacBcHmacSha3_512Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } /// public override int MacLength => MAC_LENGTH; @@ -45,7 +44,7 @@ public MacHmacSha3_512Algorithm() : base(ALGORITHM_NAME, ALGORITHM_VALUE) { } /// protected override KeyedHashAlgorithm GetMacAlgorithmInt(byte[] pwd, CryptoOptions? options) { - IMac mac = new HMac(new Sha3Digest(MAC_LENGTH << 3)); + HMac mac = new(new Sha3Digest(MAC_LENGTH << 3)); mac.Init(new KeyParameter(pwd)); return new BouncyCastleHmacAlgorithm(mac); } diff --git a/src/wan24-Crypto-BC/README.md b/src/wan24-Crypto-BC/README.md index a3e1cbf..2246810 100644 --- a/src/wan24-Crypto-BC/README.md +++ b/src/wan24-Crypto-BC/README.md @@ -14,6 +14,10 @@ the `wan24-Crypto` library with these algorithms: | SPHINCS+ | 5 | SPHINCSPLUS | | FrodoKEM* | 6 | FRODOKEM | | NTRUEncrypt* | 7 | NTRUENCRYPT | +| Ed25519 | 8 | ED25519 | +| Ed448 | 9 | ED448 | +| X25519 | 10 | X25519 | +| X448 | 11 | X448 | | **Symmetric** | | | | ChaCha20 | 1 | CHACHA20 | | XSalsa20 | 2 | XSALSA20 | @@ -22,14 +26,6 @@ the `wan24-Crypto` library with these algorithms: | Serpent 256 GCM AEAD (128 bit MAC) | 6 | SERPENT256GCM | | Twofish 256 CBC (ISO10126 padding) | 7 | TWOFISH256CBC | | Twofish 256 GCM AEAD (128 bit MAC) | 8 | TWOFISH256GCM | -| **Hashing** | | | -| SHA3-256 | 5 | SHA3-256 | -| SHA3-384 | 6 | SHA3-384 | -| SHA3-512 | 7 | SHA3-512 | -| **MAC** | | | -| HMAC-SHA3-256 | 4 | HMAC-SHA3-256 | -| HMAC-SHA3-384 | 5 | HMAC-SHA3-384 | -| HMAC-SHA3-512 | 6 | HMAC-SHA3-512 | **NOTE**: FrodoKEM and NTRUEncrypt are currently disabled, 'cause there seems to be a bug (missing code) in the Bouncy Castle library for @@ -64,12 +60,29 @@ BouncyCastle.SetDefaults(); Per default the current `wan24-Crypto` default will be set as counter algorithms to `HybridAlgorithmHelper`. +Current Bouncy Castle default algorithms are: + +- Key exchange: NTRUEncrypt +- Signature: CRYSTALS-Dilithium +- Encryption: Serpent 256 bit CBC +- PAKE encryption: Serpent 256 bit GCM + Some algorithms of the `wan24-Crypto` library are not available on some platforms, that's why they need to be replaced in order to be used: | `wan24-Crypto` | `wan24-Crypto-BC` | | -------------- | ----------------- | +| `AsymmetricEcDiffieHellmanAlgorithm` | `AsymmetricBcEcDiffieHellmanAlgorithm` | +| `AsymmetricEcDsaAlgorithm` | `AsymmetricBcEcDsaAlgorithm` | | `EncryptionAes256CbcAlgorithm` | `EncryptionBcAes256CbcAlgorithm` | +| `HashShake128Algorithm` | `HashBcShake128Algorithm` | +| `HashShake256Algorithm` | `HashBcShake256Algorithm` | +| `HashSha3_256Algorithm` | `HashBcSha3_256Algorithm` | +| `HashSha3_384Algorithm` | `HashBcSha3_384Algorithm` | +| `HashSha3_512Algorithm` | `HashBcSha3_512Algorithm` | +| `MacHmacSha3_256Algorithm` | `MacBcHmacSha3_256Algorithm` | +| `MacHmacSha3_384Algorithm` | `MacBcHmacSha3_384Algorithm` | +| `MacHmacSha3_512Algorithm` | `MacBcHmacSha3_512Algorithm` | To replace all of them: @@ -77,15 +90,20 @@ To replace all of them: BouncyCastle.ReplaceNetAlgorithms(); ``` +**NOTE**: The Shake128/256 replacements don't support variable output length +and use the default output length of the `wan24-Crypto` implementations +instead. + ## Post quantum safety -These algorithms are designed for post quantum cryptography: +These asymmetric algorithms are designed for post quantum cryptography: - CRYSTALS-Kyber (key exchange) - CRYSTALS-Dilithium (signature) - FALCON (signature) - SPHINCS+ (signature) - FrodoKEM (key exchange) +- NTRUEncrypt (key exchange) Normally you want to use them in hybrid mode and use classical algorithms of the `wan24-Crypto` package as counter algorithm. To do this per default: @@ -98,12 +116,16 @@ CryptoHelper.ForcePostQuantumSafety(); This will use these algorithms as (counter) algorithms for asymmetric cryptography, in case you didn't define other post quantum algorithms already: -- CRYSTALS-Kyber (key exchange) +- NTRUEncrypt (key exchange) - CRYSTALS-Dilithium (signature) For using other algorithms instead: ```cs +// CRYSTALS-Kyber +HybridAlgorithmHelper.SignatureAlgorithm = + AsymmetricHelper.GetAlgorithm(AsymmetricKyberAlgorithm.ALGORITHM_NAME); + // FALCON HybridAlgorithmHelper.SignatureAlgorithm = AsymmetricHelper.GetAlgorithm(AsymmetricFalconAlgorithm.ALGORITHM_NAME); @@ -123,7 +145,7 @@ encryption: ```cs // Create options having a counter private key CryptoOptions options = EncryptionHelper.GetDefaultOptions(); -options.SetCounterPrivateKey(yourKyberPrivateKey); +options.SetCounterPrivateKey(yourNtruPrivateKey); // Encrypt using the options and your normal private key byte[] cipherData = rawData.Encrypt(yourNormalPrivateKey, options); @@ -209,3 +231,17 @@ used, if `/dev/random` was preferred. To disable `/dev/random`, set **NOTE**: Currently only stream ciphers are supported, because the cipher RNG implementation doesn't buffer pre-generated random data. + +## X/Ed448-Goldilocks and X/Ed25519 + +Just a short note on Curve448: Private and public keys have a different key +size: The private key has 456 bit, while the public key has 448 bit. Both key +sizes are supported for key generation and result in the same key sizes for +the private (456 bit) and the public (448 bit) key. The private key of a key +pair will always identify with 456 bit, while the public key will always +identify with 448 bit - no matter which key size was chosen for key pair +generation. + +The Ed448 signature is context based, but currently only an empty byte array +is being used as context data. Instead of a context you should use the purpose +free text, which can be given to the signature methods of `wan24-Crypto`. diff --git a/src/wan24-Crypto-BC/StreamCipherRng.cs b/src/wan24-Crypto-BC/StreamCipherRng.cs index 170bba2..6288744 100644 --- a/src/wan24-Crypto-BC/StreamCipherRng.cs +++ b/src/wan24-Crypto-BC/StreamCipherRng.cs @@ -181,8 +181,7 @@ public virtual void NextBytes(Span bytes) RNG.FillBytes(bytes[..write]); Encryption.CryptoStream.Write(bytes[..write]); Encryption.CryptoStream.Flush(); - if (Buffer.Read(bytes[..write]) != write) - throw CryptographicException.From("Failed to read all random data from the buffer", new InvalidProgramException()); + Buffer.ReadExactly(bytes[..write]); } } diff --git a/src/wan24-Crypto-BC/XSalsa20Rng.cs b/src/wan24-Crypto-BC/XSalsa20Rng.cs index ba2c24d..7243e61 100644 --- a/src/wan24-Crypto-BC/XSalsa20Rng.cs +++ b/src/wan24-Crypto-BC/XSalsa20Rng.cs @@ -3,16 +3,14 @@ /// /// XSalsa20 CSRNG /// - public sealed class XSalsa20Rng : StreamCipherRng + /// + /// Constructor + /// + /// Internal RNG to use (will be disposed, if possible!) + /// Buffer size in bytes (min. ) + /// Seed the given RNG with N byte from (skipped, if or <1) + public sealed class XSalsa20Rng(in ISeedableRng? rng = null, in int? bufferSize = null, in int? seedLength = 256) + : StreamCipherRng(EncryptionXSalsa20Algorithm.Instance, rng, bufferSize, seedLength) { - /// - /// Constructor - /// - /// Internal RNG to use (will be disposed, if possible!) - /// Buffer size in bytes (min. ) - /// Seed the given RNG with N byte from (skipped, if or <1) - public XSalsa20Rng(in ISeedableRng? rng = null, in int? bufferSize = null, in int? seedLength = 256) - : base(EncryptionXSalsa20Algorithm.Instance, rng, bufferSize, seedLength) - { } } } diff --git a/src/wan24-Crypto-BC/wan24-Crypto-BC.csproj b/src/wan24-Crypto-BC/wan24-Crypto-BC.csproj index c0c93d9..42095a3 100644 --- a/src/wan24-Crypto-BC/wan24-Crypto-BC.csproj +++ b/src/wan24-Crypto-BC/wan24-Crypto-BC.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 wan24.Crypto.BC enable enable @@ -9,7 +9,7 @@ True wan24-Crypto-BC wan24-Crypto-BC - 1.19.3 + 2.0.0 nd1012 Andreas Zimmermann, wan24.de wan24-Crypto-BC @@ -33,8 +33,8 @@ - - + +