diff --git a/.github/workflows/docfx.yml b/.github/workflows/docfx.yml index 193d0da..4a849c9 100644 --- a/.github/workflows/docfx.yml +++ b/.github/workflows/docfx.yml @@ -18,6 +18,8 @@ jobs: dotnet-version: 8.0.x - name: Setup docfx run: dotnet tool update -g docfx + - name: Copy README + run: cp README.md "./src/wan24-Crypto-BC Docs/index.md" - name: Build docs run: docfx "./src/wan24-Crypto-BC Docs/docfx.json" - name: Commit diff --git a/README.md b/README.md index 5d91cef..4b73b52 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,18 @@ the `wan24-Crypto` library with these algorithms: | CRYSTALS-Dilithium | 3 | CRYSTALSDILITHIUM | | FALCON | 4 | FALCON | | SPHINCS+ | 5 | SPHINCSPLUS | -| FrodoKEM* | 6 | FRODOKEM | -| NTRUEncrypt* | 7 | NTRUENCRYPT | +| FrodoKEM | 6 | FRODOKEM | +| NTRUEncrypt | 7 | NTRUENCRYPT | | Ed25519 | 8 | ED25519 | | Ed448 | 9 | ED448 | | X25519 | 10 | X25519 | | X448 | 11 | X448 | | XEd25519 | 12 | XED25519 | | XEd448 | 13 | XED448 | +| Streamlined NTRU Prime | 14 | SNTRUP | +| BIKE | 15 | BIKE | +| HQC | 16 | HQC | +| Picnic | 17 | PICNIC | | **Symmetric** | | | | ChaCha20 | 1 | CHACHA20 | | XSalsa20 | 2 | XSALSA20 | @@ -29,14 +33,10 @@ the `wan24-Crypto` library with these algorithms: | Twofish 256 CBC (ISO10126 padding) | 7 | TWOFISH256CBC | | Twofish 256 GCM AEAD (128 bit MAC) | 8 | TWOFISH256GCM | -NTRUSign is currently not implemented, 'cause it'd require the using code to -be GPL licensed. This algorithm may be included in a separate package which is -licensed using the GPL license (to avoid misunderstandings) in the future. - -**NOTE**: SPHINCS+ and NTRUEncrypt key serialization uses a custom serializer -at present, which will change in the future, as soon as Bouncy Castle -implemented a (working) serializer (again). This change will require a manual -key conversion from the current serialization format. +Main goals of this extension library are to make `wan24-Crypto` usable on all +platforms and extend its algorithms by PQC algorithms and other non-PQC +algorithms, which are not available from .NET, but implemented in the Bouncy +Castle library. ## How to get it @@ -54,21 +54,7 @@ wan24.Crypto.BC.Bootstrap.Boot(); This will register the algorithms to the `wan24-Crypto` library. -To set Bouncy Castle defaults as `wan24-Crypto` defaults: - -```cs -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 +### `wan24-Crypto` algorithm replacement 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: @@ -95,7 +81,28 @@ 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. +instead. The `NetShake128/256HashAlgorithmAdapter` can't be replaced for this +reason. + +### Use as default algorithms + +To set Bouncy Castle defaults as `wan24-Crypto` defaults: + +```cs +BouncyCastle.SetDefaults(); +``` + +Per default the current `wan24-Crypto` default will be set as counter +algorithms to `HybridAlgorithmHelper`. + +Current Bouncy Castle default algorithms are: + +| Usage | Algorithm | +| ----- | --------- | +| Key exchange | NTRUEncrypt | +| Signature | CRYSTALS-Dilithium | +| Encryption | Serpent 256 bit CBC | +| PAKE encryption | Serpent 256 bit GCM | ## Post quantum safety @@ -107,6 +114,10 @@ These asymmetric algorithms are designed for post quantum cryptography: - SPHINCS+ (signature) - FrodoKEM (key exchange) - NTRUEncrypt (key exchange) +- Streamlined NTRU Prime (key exchange) +- BIKE (key exchange) +- HQC (key exchange) +- Picnic (signature) 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: @@ -148,11 +159,23 @@ SignatureContainer signature = dataToSign.Sign(yourNormalPrivateKey, options: op ## Algorithm parameters used -For CRYSTALS-Kyber and CRYSTALS-Dilithium the non-AES parameters are being -used, since the AES parameter sets have been deprecated. When using SPHINCS+, -the Haraka simple hashing parameters will be used (since the Haraka robust -hashing parameters have been reprecated). For FrodoKEM the AES parameters will -be used. +| Algorithm | Parameters | +| --------- | ---------- | +| CRYSTALS-Kyber, CRYSTALS-Dilithium | non-AES | +| SPHINCS+ | Haraka simple* | +| FrodoKEM | AES* | +| Picnic | Full | + +**NOTE**: CRYSTALS-Kyber and CRYSTALS-Dilithium AES parameters and SPHINCS+ +robust parameters are deprecated! SPHINCS+ Haraka parameters are removed from +the FIPS standard, so `wan24-Crypto-BC` will switch to Shake parameters +instead. Also the FrodoKEM Shake parameters will be used in the next major +release, which will require to renew existing keys, which use the AES +parameters from the current version of this library. + +**WARNING** The PQC standards are in development at the moment, so future +incompatible changes are very likely and will be handled in a new major +release of this library. ## Random data provider @@ -186,8 +209,10 @@ bytes of an underlaying PRNG using a random key. The result is a CSRNG. These stream ciphers are available with `wan24-Crypto-BC`, but you could use any other stream cipher (but not AEAD implementations!) also: -- ChaCha20 - `ChaCha20Rng` -- XSalsa20 - `XSalsa20Rng` +| Stream cipher | RNG | +| ------------- | --- | +| ChaCha20 | `ChaCha20Rng` | +| XSalsa20 | `XSalsa20Rng` | If you didn't specify an underlaying PRNG, Bouncy Castle's `VmpcRandomGenerator` will be used and seeded using 256 bytes from `RND`. 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 946a44a..d1d7dd9 100644 --- a/src/wan24-Crypto-BC Tests/wan24-Crypto-BC Tests.csproj +++ b/src/wan24-Crypto-BC Tests/wan24-Crypto-BC Tests.csproj @@ -15,13 +15,13 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPrivateKey.cs index b30524d..7b28b53 100644 --- a/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricBcEcDiffieHellmanPrivateKey.cs @@ -35,6 +35,12 @@ public AsymmetricBcEcDiffieHellmanPrivateKey(byte[] keyData) : base(AsymmetricBc /// Keys public AsymmetricBcEcDiffieHellmanPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricBcEcDiffieHellmanAlgorithm.ALGORITHM_NAME, keys) { } + /// + /// Constructor + /// + /// Private key + public AsymmetricBcEcDiffieHellmanPrivateKey(ECPrivateKeyParameters privateKey) : base(AsymmetricBcEcDiffieHellmanAlgorithm.ALGORITHM_NAME, privateKey) { } + /// public override (byte[] Key, byte[] KeyExchangeData) GetKeyExchangeData(IAsymmetricPublicKey? publicKey = null, CryptoOptions? options = null) { diff --git a/src/wan24-Crypto-BC/AsymmetricBcEcDsaPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricBcEcDsaPrivateKey.cs index 8a345d7..7a28709 100644 --- a/src/wan24-Crypto-BC/AsymmetricBcEcDsaPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricBcEcDsaPrivateKey.cs @@ -35,6 +35,12 @@ public AsymmetricBcEcDsaPrivateKey(byte[] keyData) : base(AsymmetricBcEcDsaAlgor /// Keys public AsymmetricBcEcDsaPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricBcEcDsaAlgorithm.ALGORITHM_NAME, keys) { } + /// + /// Constructor + /// + /// Private key + public AsymmetricBcEcDsaPrivateKey(ECPrivateKeyParameters privateKey) : base(AsymmetricBcEcDsaAlgorithm.ALGORITHM_NAME, privateKey) { } + /// protected override ECPublicKeyParameters GetPublicKey(ECPrivateKeyParameters privateKey) => new(privateKey.Parameters.G.Multiply(privateKey.D), privateKey.Parameters); diff --git a/src/wan24-Crypto-BC/AsymmetricBikeAlgorithm.cs b/src/wan24-Crypto-BC/AsymmetricBikeAlgorithm.cs new file mode 100644 index 0000000..bcfc707 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricBikeAlgorithm.cs @@ -0,0 +1,70 @@ +using Org.BouncyCastle.Pqc.Crypto.Bike; +using System.Collections.ObjectModel; + +namespace wan24.Crypto.BC +{ + /// + /// BIKE asymmetric algorithm + /// + public sealed record class AsymmetricBikeAlgorithm + : BouncyCastleAsymmetricAlgorithmBase< + AsymmetricBikePublicKey, + AsymmetricBikePrivateKey, + BikeKeyPairGenerator, + BikeKeyGenerationParameters, + BikeParameters, + BikePublicKeyParameters, + BikePrivateKeyParameters, + AsymmetricBikeAlgorithm + > + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "BIKE"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 15; + /// + /// 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 = "BIKE"; + + /// + /// Allowed key sizes in bits + /// + private static readonly ReadOnlyCollection _AllowedKeySizes; + + /// + /// Static constructor + /// + static AsymmetricBikeAlgorithm() => _AllowedKeySizes = new List() + { + 128,// 128 bit security + 192,// 192 bit security + 256// 256 bit security + }.AsReadOnly(); + + /// + /// Constructor + /// + public AsymmetricBikeAlgorithm() + : base(ALGORITHM_NAME, ALGORITHM_VALUE, USAGES, isEllipticCurveAlgorithm: false, _AllowedKeySizes, isPostQuantum: true, DEFAULT_KEY_SIZE) + { } + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + protected override BikeParameters GetEngineParameters(CryptoOptions options) => AsymmetricBikeHelper.GetParameters(options.AsymmetricKeyBits); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricBikeHelper.cs b/src/wan24-Crypto-BC/AsymmetricBikeHelper.cs new file mode 100644 index 0000000..2d4159a --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricBikeHelper.cs @@ -0,0 +1,36 @@ +using Org.BouncyCastle.Pqc.Crypto.Bike; + +namespace wan24.Crypto.BC +{ + /// + /// BIKE asymmetric algorithm helper + /// + public static class AsymmetricBikeHelper + { + /// + /// Get the key size in bits + /// + /// Parameters + /// Key size in bits + public static int GetKeySize(this BikeParameters param) + { + if (param == BikeParameters.bike128) return 128; + if (param == BikeParameters.bike192) return 192; + if (param == BikeParameters.bike256) return 256; + throw new ArgumentException("Invalid BIKE parameters", nameof(param)); + } + + /// + /// Get the BIKE parameters + /// + /// Key size in bits + /// Parameters + public static BikeParameters GetParameters(int keySize) => keySize switch + { + 128 => BikeParameters.bike128, + 192 => BikeParameters.bike192, + 256 => BikeParameters.bike256, + _ => throw new ArgumentException("Invalid key size", nameof(keySize)) + }; + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricBikePrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricBikePrivateKey.cs new file mode 100644 index 0000000..0206fdd --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricBikePrivateKey.cs @@ -0,0 +1,82 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Pqc.Crypto.Bike; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// BIKE asymmetric private key + /// + public sealed record class AsymmetricBikePrivateKey + : BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase< + AsymmetricBikePublicKey, + AsymmetricBikeAlgorithm, + BikePublicKeyParameters, + BikePrivateKeyParameters, + BikeKemGenerator, + BikeKemExtractor, + AsymmetricBikePrivateKey + > + { + /// + /// Constructor + /// + public AsymmetricBikePrivateKey() : base(AsymmetricBikeAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricBikePrivateKey(byte[] keyData) : base(AsymmetricBikeAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Keys + public AsymmetricBikePrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricBikeAlgorithm.ALGORITHM_NAME, keys) { } + + /// + new public static bool IsBcImportExportImplemented => false; + + /// + protected override byte[] SerializeKeyData() => SerializeFullKeyData(); + + /// + protected override void DeserializeKeyData() => DeserializeFullKeyData(); + + /// + protected override BikePublicKeyParameters GetPublicKey(BikePrivateKeyParameters privateKey) => throw new NotSupportedException(); + + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (Keys?.Private is not BikePrivateKeyParameters privateKey) return; + privateKey.GetH0().Clear(); + privateKey.GetH1().Clear(); + privateKey.GetSigma().Clear(); + } + + /// + protected override async Task DisposeCore() + { + await base.DisposeCore().DynamicContext(); + if (Keys?.Private is not BikePrivateKeyParameters privateKey) return; + privateKey.GetH0().Clear(); + privateKey.GetH1().Clear(); + privateKey.GetSigma().Clear(); + } + + /// + /// Cast to public key + /// + /// Private key + public static implicit operator AsymmetricBikePublicKey(AsymmetricBikePrivateKey privateKey) => privateKey.PublicKey; + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricBikePrivateKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricBikePublicKey.cs b/src/wan24-Crypto-BC/AsymmetricBikePublicKey.cs new file mode 100644 index 0000000..8481007 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricBikePublicKey.cs @@ -0,0 +1,58 @@ +using Org.BouncyCastle.Pqc.Crypto.Bike; + +namespace wan24.Crypto.BC +{ + /// + /// BIKE asymmetric public key + /// + public sealed record class AsymmetricBikePublicKey : BouncyCastleAsymmetricPqcPublicKeyBase + { + /// + /// Constructor + /// + public AsymmetricBikePublicKey() : base(AsymmetricBikeAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricBikePublicKey(byte[] keyData) : base(AsymmetricBikeAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Public key + public AsymmetricBikePublicKey(BikePublicKeyParameters publicKey) : base(AsymmetricBikeAlgorithm.ALGORITHM_NAME, publicKey) { } + + /// + new public static bool IsBcImportExportImplemented => false; + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + if (_PublicKey is null) throw new InvalidOperationException(); + return AsymmetricBikeHelper.GetKeySize(_PublicKey.Parameters); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricBikePublicKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricDilithiumAlgorithm.cs b/src/wan24-Crypto-BC/AsymmetricDilithiumAlgorithm.cs index 50a1844..1dcf41f 100644 --- a/src/wan24-Crypto-BC/AsymmetricDilithiumAlgorithm.cs +++ b/src/wan24-Crypto-BC/AsymmetricDilithiumAlgorithm.cs @@ -49,9 +49,9 @@ public sealed record class AsymmetricDilithiumAlgorithm /// static AsymmetricDilithiumAlgorithm() => _AllowedKeySizes = new List() { - 512, - 768, - 1024 + 512,// 128 bit security + 768,// 192 bit security + 1024// 256 bit security }.AsReadOnly(); /// diff --git a/src/wan24-Crypto-BC/AsymmetricDilithiumPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricDilithiumPrivateKey.cs index 2750eff..e921764 100644 --- a/src/wan24-Crypto-BC/AsymmetricDilithiumPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricDilithiumPrivateKey.cs @@ -34,6 +34,12 @@ public AsymmetricDilithiumPrivateKey(byte[] keyData) : base(AsymmetricDilithiumA /// Keys public AsymmetricDilithiumPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricDilithiumAlgorithm.ALGORITHM_NAME, keys) { } + /// + /// Constructor + /// + /// Private key + public AsymmetricDilithiumPrivateKey(DilithiumPrivateKeyParameters privateKey) : base(AsymmetricDilithiumAlgorithm.ALGORITHM_NAME, privateKey) { } + /// protected override DilithiumPublicKeyParameters GetPublicKey(DilithiumPrivateKeyParameters privateKey) => new(privateKey.Parameters, privateKey.Rho, privateKey.T1); diff --git a/src/wan24-Crypto-BC/AsymmetricDilithiumPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricDilithiumPublicKey.cs index f73472c..3dedb00 100644 --- a/src/wan24-Crypto-BC/AsymmetricDilithiumPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricDilithiumPublicKey.cs @@ -33,7 +33,8 @@ public override int Bits try { EnsureUndisposed(); - return _PublicKey?.Parameters.GetKeySize() ?? throw new InvalidOperationException(); + if (_PublicKey is null) throw new InvalidOperationException(); + return AsymmetricDilithiumHelper.GetKeySize(_PublicKey.Parameters); } catch (CryptographicException) { diff --git a/src/wan24-Crypto-BC/AsymmetricEd25519Algorithm.cs b/src/wan24-Crypto-BC/AsymmetricEd25519Algorithm.cs index 803d05b..4e21fe3 100644 --- a/src/wan24-Crypto-BC/AsymmetricEd25519Algorithm.cs +++ b/src/wan24-Crypto-BC/AsymmetricEd25519Algorithm.cs @@ -8,7 +8,7 @@ namespace wan24.Crypto.BC { /// - /// Ed25519 asymmetric algorithm + /// Ed25519 asymmetric algorithm (128 bit security) /// public sealed record class AsymmetricEd25519Algorithm : BouncyCastleAsymmetricAlgorithmBase< diff --git a/src/wan24-Crypto-BC/AsymmetricEd25519PrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricEd25519PrivateKey.cs index 62e4338..b5c3876 100644 --- a/src/wan24-Crypto-BC/AsymmetricEd25519PrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricEd25519PrivateKey.cs @@ -35,6 +35,12 @@ public AsymmetricEd25519PrivateKey(byte[] keyData) : base(AsymmetricEd25519Algor /// Keys public AsymmetricEd25519PrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricEd25519Algorithm.ALGORITHM_NAME, keys) { } + /// + /// Constructor + /// + /// Private key + public AsymmetricEd25519PrivateKey(Ed25519PrivateKeyParameters privateKey) : base(AsymmetricEd25519Algorithm.ALGORITHM_NAME, privateKey) { } + /// /// Create a XEd25519 private key instance /// diff --git a/src/wan24-Crypto-BC/AsymmetricEd448Algorithm.cs b/src/wan24-Crypto-BC/AsymmetricEd448Algorithm.cs index 7a27d31..ef34d50 100644 --- a/src/wan24-Crypto-BC/AsymmetricEd448Algorithm.cs +++ b/src/wan24-Crypto-BC/AsymmetricEd448Algorithm.cs @@ -8,7 +8,7 @@ namespace wan24.Crypto.BC { /// - /// Ed448 asymmetric algorithm + /// Ed448 asymmetric algorithm (224 bit security) /// public sealed record class AsymmetricEd448Algorithm : BouncyCastleAsymmetricAlgorithmBase< diff --git a/src/wan24-Crypto-BC/AsymmetricEd448PrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricEd448PrivateKey.cs index 915a736..5a53347 100644 --- a/src/wan24-Crypto-BC/AsymmetricEd448PrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricEd448PrivateKey.cs @@ -35,6 +35,12 @@ public AsymmetricEd448PrivateKey(byte[] keyData) : base(AsymmetricEd448Algorithm /// Keys public AsymmetricEd448PrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricEd448Algorithm.ALGORITHM_NAME, keys) { } + /// + /// Constructor + /// + /// Private key + public AsymmetricEd448PrivateKey(Ed448PrivateKeyParameters privateKey) : base(AsymmetricEd448Algorithm.ALGORITHM_NAME, privateKey) { } + /// public override int Bits { diff --git a/src/wan24-Crypto-BC/AsymmetricFalconAlgorithm.cs b/src/wan24-Crypto-BC/AsymmetricFalconAlgorithm.cs index 68b1ca6..5a320b2 100644 --- a/src/wan24-Crypto-BC/AsymmetricFalconAlgorithm.cs +++ b/src/wan24-Crypto-BC/AsymmetricFalconAlgorithm.cs @@ -49,8 +49,8 @@ public sealed record class AsymmetricFalconAlgorithm /// static AsymmetricFalconAlgorithm() => _AllowedKeySizes = new List() { - 512, - 1024 + 512,// 128 bit security + 1024// 224 bit security }.AsReadOnly(); /// diff --git a/src/wan24-Crypto-BC/AsymmetricFalconPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricFalconPrivateKey.cs index 241658e..a6f417d 100644 --- a/src/wan24-Crypto-BC/AsymmetricFalconPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricFalconPrivateKey.cs @@ -34,6 +34,12 @@ public AsymmetricFalconPrivateKey(byte[] keyData) : base(AsymmetricFalconAlgorit /// Keys public AsymmetricFalconPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricFalconAlgorithm.ALGORITHM_NAME, keys) { } + /// + /// Constructor + /// + /// Private key + public AsymmetricFalconPrivateKey(FalconPrivateKeyParameters privateKey) : base(AsymmetricFalconAlgorithm.ALGORITHM_NAME, privateKey) { } + /// protected override FalconPublicKeyParameters GetPublicKey(FalconPrivateKeyParameters privateKey) => new(privateKey.Parameters, privateKey.GetPublicKey()); diff --git a/src/wan24-Crypto-BC/AsymmetricFalconPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricFalconPublicKey.cs index f54b369..7a50148 100644 --- a/src/wan24-Crypto-BC/AsymmetricFalconPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricFalconPublicKey.cs @@ -32,7 +32,8 @@ public override int Bits try { EnsureUndisposed(); - return _PublicKey?.Parameters.GetKeySize() ?? throw new InvalidOperationException(); + if (_PublicKey is null) throw new InvalidOperationException(); + return AsymmetricFalconHelper.GetKeySize(_PublicKey.Parameters); } catch (CryptographicException) { diff --git a/src/wan24-Crypto-BC/AsymmetricFrodoKemHelper.cs b/src/wan24-Crypto-BC/AsymmetricFrodoKemHelper.cs index 613e675..18da683 100644 --- a/src/wan24-Crypto-BC/AsymmetricFrodoKemHelper.cs +++ b/src/wan24-Crypto-BC/AsymmetricFrodoKemHelper.cs @@ -1,5 +1,7 @@ using Org.BouncyCastle.Pqc.Crypto.Frodo; +//TODO Use FalconKEM Shake parameter sets in newer version, use key complexity instead of session key size in bits as "key size" + namespace wan24.Crypto.BC { /// diff --git a/src/wan24-Crypto-BC/AsymmetricFrodoKemPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricFrodoKemPrivateKey.cs index 3e589e9..305fdf8 100644 --- a/src/wan24-Crypto-BC/AsymmetricFrodoKemPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricFrodoKemPrivateKey.cs @@ -1,10 +1,6 @@ -using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Crypto; +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 { @@ -40,70 +36,13 @@ public AsymmetricFrodoKemPrivateKey(byte[] keyData) : base(AsymmetricFrodoKemAlg public AsymmetricFrodoKemPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricFrodoKemAlgorithm.ALGORITHM_NAME, keys) { } /// - protected override byte[] SerializeKeyData() - { - // Custom serialization required, because there seems to be no way to derive the public key from the private key - try - { - EnsureUndisposed(); - if (Keys == null) throw new InvalidOperationException(); - using MemoryPoolStream ms = new() - { - CleanReturned = true - }; - PrivateKeyInfo privateKeyInfo = PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo(Keys.Private); - using SecureByteArray privateKey = new(privateKeyInfo.GetDerEncoded()); - SubjectPublicKeyInfo publicKeyInfo = PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(Keys.Public); - using SecureByteArray publicKey = new(publicKeyInfo.GetDerEncoded()); - ms.WriteSerializerVersion() - .WriteBytes(privateKey.Array) - .WriteBytes(publicKey.Array); - return ms.ToArray(); - } - catch (CryptographicException) - { - throw; - } - catch (Exception ex) - { - throw CryptographicException.From(ex); - } - } + new public static bool IsBcImportExportImplemented => false; /// - protected override void DeserializeKeyData() - { - try - { - EnsureUndisposed(); - FrodoPrivateKeyParameters? privateKey = null; - FrodoPublicKeyParameters? publicKey = null; - try - { - using MemoryStream ms = new(KeyData.Array); - int ssv = ms.ReadSerializerVersion(); - using SecureByteArrayRefStruct privateKeyInfo = new(ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); - privateKey = PqcPrivateKeyFactory.CreateKey(privateKeyInfo.Array) as FrodoPrivateKeyParameters ?? throw new InvalidDataException("Invalid private FrodoKEM key"); - using SecureByteArrayRefStruct publicKeyInfo = new(ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); - publicKey = PqcPublicKeyFactory.CreateKey(publicKeyInfo.Array) as FrodoPublicKeyParameters ?? throw new InvalidDataException("Invalid public FrodoKEM key"); - Keys = new(publicKey, privateKey); - } - catch - { - privateKey?.ClearPrivateByteArrayFields(); - publicKey?.ClearPrivateByteArrayFields(); - throw; - } - } - catch (CryptographicException) - { - throw; - } - catch (Exception ex) - { - throw CryptographicException.From(ex); - } - } + protected override byte[] SerializeKeyData() => SerializeFullKeyData(); + + /// + protected override void DeserializeKeyData() => DeserializeFullKeyData(); /// protected override FrodoPublicKeyParameters GetPublicKey(FrodoPrivateKeyParameters privateKey) => throw new NotSupportedException(); diff --git a/src/wan24-Crypto-BC/AsymmetricFrodoKemPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricFrodoKemPublicKey.cs index 511f69c..d9e9a0f 100644 --- a/src/wan24-Crypto-BC/AsymmetricFrodoKemPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricFrodoKemPublicKey.cs @@ -24,6 +24,9 @@ public AsymmetricFrodoKemPublicKey(byte[] keyData) : base(AsymmetricFrodoKemAlgo /// Public key public AsymmetricFrodoKemPublicKey(FrodoPublicKeyParameters publicKey) : base(AsymmetricFrodoKemAlgorithm.ALGORITHM_NAME, publicKey) { } + /// + new public static bool IsBcImportExportImplemented => false; + /// public override int Bits { @@ -32,7 +35,8 @@ public override int Bits try { EnsureUndisposed(); - return _PublicKey?.Parameters.GetKeySize() ?? throw new InvalidOperationException(); + if (_PublicKey is null) throw new InvalidOperationException(); + return AsymmetricFrodoKemHelper.GetKeySize(_PublicKey.Parameters); } catch (CryptographicException) { diff --git a/src/wan24-Crypto-BC/AsymmetricHqcAlgorithm.cs b/src/wan24-Crypto-BC/AsymmetricHqcAlgorithm.cs new file mode 100644 index 0000000..c890a4e --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricHqcAlgorithm.cs @@ -0,0 +1,70 @@ +using Org.BouncyCastle.Pqc.Crypto.Hqc; +using System.Collections.ObjectModel; + +namespace wan24.Crypto.BC +{ + /// + /// HQC asymmetric algorithm + /// + public sealed record class AsymmetricHqcAlgorithm + : BouncyCastleAsymmetricAlgorithmBase< + AsymmetricHqcPublicKey, + AsymmetricHqcPrivateKey, + HqcKeyPairGenerator, + HqcKeyGenerationParameters, + HqcParameters, + HqcPublicKeyParameters, + HqcPrivateKeyParameters, + AsymmetricHqcAlgorithm + > + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "HQC"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 16; + /// + /// 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 = "HQC"; + + /// + /// Allowed key sizes in bits + /// + private static readonly ReadOnlyCollection _AllowedKeySizes; + + /// + /// Static constructor + /// + static AsymmetricHqcAlgorithm() => _AllowedKeySizes = new List() + { + 128,// 128 bit security + 192,// 192 bit security + 256// 256 bit security + }.AsReadOnly(); + + /// + /// Constructor + /// + public AsymmetricHqcAlgorithm() + : base(ALGORITHM_NAME, ALGORITHM_VALUE, USAGES, isEllipticCurveAlgorithm: false, _AllowedKeySizes, isPostQuantum: true, DEFAULT_KEY_SIZE) + { } + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + protected override HqcParameters GetEngineParameters(CryptoOptions options) => AsymmetricHqcHelper.GetParameters(options.AsymmetricKeyBits); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricHqcHelper.cs b/src/wan24-Crypto-BC/AsymmetricHqcHelper.cs new file mode 100644 index 0000000..0f4f1c4 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricHqcHelper.cs @@ -0,0 +1,36 @@ +using Org.BouncyCastle.Pqc.Crypto.Hqc; + +namespace wan24.Crypto.BC +{ + /// + /// HQC asymmetric algorithm helper + /// + public static class AsymmetricHqcHelper + { + /// + /// Get the key size in bits + /// + /// Parameters + /// Key size in bits + public static int GetKeySize(this HqcParameters param) + { + if (param == HqcParameters.hqc128) return 128; + if (param == HqcParameters.hqc192) return 192; + if (param == HqcParameters.hqc256) return 256; + throw new ArgumentException("Invalid HQC parameters", nameof(param)); + } + + /// + /// Get the HQC parameters + /// + /// Key size in bits + /// Parameters + public static HqcParameters GetParameters(int keySize) => keySize switch + { + 128 => HqcParameters.hqc128, + 192 => HqcParameters.hqc192, + 256 => HqcParameters.hqc256, + _ => throw new ArgumentException("Invalid key size", nameof(keySize)) + }; + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricHqcPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricHqcPrivateKey.cs new file mode 100644 index 0000000..a1586b8 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricHqcPrivateKey.cs @@ -0,0 +1,76 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Pqc.Crypto.Hqc; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// HQC asymmetric private key + /// + public sealed record class AsymmetricHqcPrivateKey + : BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase< + AsymmetricHqcPublicKey, + AsymmetricHqcAlgorithm, + HqcPublicKeyParameters, + HqcPrivateKeyParameters, + HqcKemGenerator, + HqcKemExtractor, + AsymmetricHqcPrivateKey + > + { + /// + /// Constructor + /// + public AsymmetricHqcPrivateKey() : base(AsymmetricHqcAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricHqcPrivateKey(byte[] keyData) : base(AsymmetricHqcAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Keys + public AsymmetricHqcPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricHqcAlgorithm.ALGORITHM_NAME, keys) { } + + /// + new public static bool IsBcImportExportImplemented => false; + + /// + protected override byte[] SerializeKeyData() => SerializeFullKeyData(); + + /// + protected override void DeserializeKeyData() => DeserializeFullKeyData(); + + /// + protected override HqcPublicKeyParameters GetPublicKey(HqcPrivateKeyParameters privateKey) => throw new NotSupportedException(); + + /// + 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 AsymmetricHqcPublicKey(AsymmetricHqcPrivateKey privateKey) => privateKey.PublicKey; + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricHqcPrivateKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricHqcPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricHqcPublicKey.cs new file mode 100644 index 0000000..6829eb9 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricHqcPublicKey.cs @@ -0,0 +1,58 @@ +using Org.BouncyCastle.Pqc.Crypto.Hqc; + +namespace wan24.Crypto.BC +{ + /// + /// HQC asymmetric public key + /// + public sealed record class AsymmetricHqcPublicKey : BouncyCastleAsymmetricPqcPublicKeyBase + { + /// + /// Constructor + /// + public AsymmetricHqcPublicKey() : base(AsymmetricHqcAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricHqcPublicKey(byte[] keyData) : base(AsymmetricHqcAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Public key + public AsymmetricHqcPublicKey(HqcPublicKeyParameters publicKey) : base(AsymmetricHqcAlgorithm.ALGORITHM_NAME, publicKey) { } + + /// + new public static bool IsBcImportExportImplemented => false; + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + if (_PublicKey is null) throw new InvalidOperationException(); + return AsymmetricHqcHelper.GetKeySize(_PublicKey.Parameters); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricHqcPublicKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricKyberAlgorithm.cs b/src/wan24-Crypto-BC/AsymmetricKyberAlgorithm.cs index 0d1c91e..f6cfc56 100644 --- a/src/wan24-Crypto-BC/AsymmetricKyberAlgorithm.cs +++ b/src/wan24-Crypto-BC/AsymmetricKyberAlgorithm.cs @@ -49,9 +49,9 @@ public sealed record class AsymmetricKyberAlgorithm /// static AsymmetricKyberAlgorithm() => _AllowedKeySizes = new List() { - 512, - 768, - 1024 + 512,// 128 bit security + 768,// 192 bit security + 1024// 256 bit security }.AsReadOnly(); /// diff --git a/src/wan24-Crypto-BC/AsymmetricKyberPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricKyberPrivateKey.cs index 4ad6038..92827e7 100644 --- a/src/wan24-Crypto-BC/AsymmetricKyberPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricKyberPrivateKey.cs @@ -35,6 +35,9 @@ public AsymmetricKyberPrivateKey(byte[] keyData) : base(AsymmetricKyberAlgorithm /// Keys public AsymmetricKyberPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricKyberAlgorithm.ALGORITHM_NAME, keys) { } + /// + new public static bool IsBcImportExportImplemented => false; + /// protected override byte[] SerializeKeyData() => SerializeFullKeyData(); diff --git a/src/wan24-Crypto-BC/AsymmetricKyberPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricKyberPublicKey.cs index 3ae7304..4a3b295 100644 --- a/src/wan24-Crypto-BC/AsymmetricKyberPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricKyberPublicKey.cs @@ -24,6 +24,9 @@ public AsymmetricKyberPublicKey(byte[] keyData) : base(AsymmetricKyberAlgorithm. /// Public key public AsymmetricKyberPublicKey(KyberPublicKeyParameters publicKey) : base(AsymmetricKyberAlgorithm.ALGORITHM_NAME, publicKey) { } + /// + new public static bool IsBcImportExportImplemented => false; + /// public override int Bits { @@ -32,7 +35,8 @@ public override int Bits try { EnsureUndisposed(); - return _PublicKey?.Parameters.GetKeySize() ?? throw new InvalidOperationException(); + if (_PublicKey is null) throw new InvalidOperationException(); + return AsymmetricKyberHelper.GetKeySize(_PublicKey.Parameters); } catch (CryptographicException) { diff --git a/src/wan24-Crypto-BC/AsymmetricNtruEncryptAlgorithm.cs b/src/wan24-Crypto-BC/AsymmetricNtruEncryptAlgorithm.cs index b99d428..51dc115 100644 --- a/src/wan24-Crypto-BC/AsymmetricNtruEncryptAlgorithm.cs +++ b/src/wan24-Crypto-BC/AsymmetricNtruEncryptAlgorithm.cs @@ -49,10 +49,10 @@ public sealed record class AsymmetricNtruEncryptAlgorithm /// static AsymmetricNtruEncryptAlgorithm() => _AllowedKeySizes = new List() { - 509, - 677, - 701, - 821 + 509,// 128 bit security + 677,// 192 bit security + 701,// 256 bit security + 821// 123 bit security }.AsReadOnly(); /// diff --git a/src/wan24-Crypto-BC/AsymmetricNtruEncryptPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricNtruEncryptPrivateKey.cs index 977a4f5..476e82e 100644 --- a/src/wan24-Crypto-BC/AsymmetricNtruEncryptPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricNtruEncryptPrivateKey.cs @@ -36,6 +36,9 @@ public AsymmetricNtruEncryptPrivateKey(byte[] keyData) : base(AsymmetricNtruEncr /// Keys public AsymmetricNtruEncryptPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricNtruEncryptAlgorithm.ALGORITHM_NAME, keys) { } + /// + new public static bool IsBcImportExportImplemented => false; + /// protected override byte[] SerializeKeyData() { diff --git a/src/wan24-Crypto-BC/AsymmetricNtruEncryptPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricNtruEncryptPublicKey.cs index 12ab9fc..99f1e3f 100644 --- a/src/wan24-Crypto-BC/AsymmetricNtruEncryptPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricNtruEncryptPublicKey.cs @@ -27,6 +27,9 @@ public AsymmetricNtruEncryptPublicKey(byte[] keyData) : base(AsymmetricNtruEncry /// Public key public AsymmetricNtruEncryptPublicKey(NtruPublicKeyParameters publicKey) : base(AsymmetricNtruEncryptAlgorithm.ALGORITHM_NAME, publicKey) { } + /// + new public static bool IsBcImportExportImplemented => false; + /// public override int Bits { @@ -35,7 +38,8 @@ public override int Bits try { EnsureUndisposed(); - return _PublicKey?.Parameters.GetKeySize() ?? throw new InvalidOperationException(); + if (_PublicKey is null) throw new InvalidOperationException(); + return AsymmetricNtruHelper.GetKeySize(_PublicKey.Parameters); } catch (CryptographicException) { diff --git a/src/wan24-Crypto-BC/AsymmetricNtruHelper.cs b/src/wan24-Crypto-BC/AsymmetricNtruHelper.cs index 8cd3a6d..dee31f7 100644 --- a/src/wan24-Crypto-BC/AsymmetricNtruHelper.cs +++ b/src/wan24-Crypto-BC/AsymmetricNtruHelper.cs @@ -1,5 +1,7 @@ using Org.BouncyCastle.Pqc.Crypto.Ntru; +//TODO PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo doesn't support NtruPrivateKeyParameters !? (waiting for a fix and an update of the NuGet package at present) + namespace wan24.Crypto.BC { /// diff --git a/src/wan24-Crypto-BC/AsymmetricPicnicAlgorithm.cs b/src/wan24-Crypto-BC/AsymmetricPicnicAlgorithm.cs new file mode 100644 index 0000000..00a94d7 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricPicnicAlgorithm.cs @@ -0,0 +1,70 @@ +using Org.BouncyCastle.Pqc.Crypto.Picnic; +using System.Collections.ObjectModel; + +namespace wan24.Crypto.BC +{ + /// + /// CRYSTALS-Dilithium asymmetric algorithm + /// + public sealed record class AsymmetricPicnicAlgorithm + : BouncyCastleAsymmetricAlgorithmBase< + AsymmetricPicnicPublicKey, + AsymmetricPicnicPrivateKey, + PicnicKeyPairGenerator, + PicnicKeyGenerationParameters, + PicnicParameters, + PicnicPublicKeyParameters, + PicnicPrivateKeyParameters, + AsymmetricPicnicAlgorithm + > + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "PICNIC"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 17; + /// + /// Default key size in bits + /// + public const int DEFAULT_KEY_SIZE = 128; + /// + /// Algorithm usages + /// + public const AsymmetricAlgorithmUsages USAGES = AsymmetricAlgorithmUsages.Signature; + /// + /// Display name + /// + public const string DISPLAY_NAME = "Picnic"; + + /// + /// Allowed key sizes in bits + /// + private static readonly ReadOnlyCollection _AllowedKeySizes; + + /// + /// Static constructor + /// + static AsymmetricPicnicAlgorithm() => _AllowedKeySizes = new List() + { + 128,// 128 bit security + 192,// 192 bit security + 256// 256 bit security + }.AsReadOnly(); + + /// + /// Constructor + /// + public AsymmetricPicnicAlgorithm() + : base(ALGORITHM_NAME, ALGORITHM_VALUE, USAGES, isEllipticCurveAlgorithm: false, _AllowedKeySizes, isPostQuantum: true, DEFAULT_KEY_SIZE) + { } + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + protected override PicnicParameters GetEngineParameters(CryptoOptions options) => AsymmetricPicnicHelper.GetParameters(options.AsymmetricKeyBits); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricPicnicHelper.cs b/src/wan24-Crypto-BC/AsymmetricPicnicHelper.cs new file mode 100644 index 0000000..5c84909 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricPicnicHelper.cs @@ -0,0 +1,36 @@ +using Org.BouncyCastle.Pqc.Crypto.Picnic; + +namespace wan24.Crypto.BC +{ + /// + /// Picnic helper + /// + public static class AsymmetricPicnicHelper + { + /// + /// Get the key size in bits + /// + /// Parameters + /// Key size in bits + public static int GetKeySize(this PicnicParameters param) + { + if (param == PicnicParameters.picnicl1full) return 128; + if (param == PicnicParameters.picnicl3full) return 192; + if (param == PicnicParameters.picnicl5full) return 256; + throw new ArgumentException("Invalid Picnic parameters", nameof(param)); + } + + /// + /// Get the Picnic parameters + /// + /// Key size in bits + /// Parameters + public static PicnicParameters GetParameters(int keySize) => keySize switch + { + 128 => PicnicParameters.picnicl1full, + 192 => PicnicParameters.picnicl3full, + 256 => PicnicParameters.picnicl5full, + _ => throw new ArgumentException("Invalid key size", nameof(keySize)) + }; + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricPicnicPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricPicnicPrivateKey.cs new file mode 100644 index 0000000..f0162d2 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricPicnicPrivateKey.cs @@ -0,0 +1,75 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Pqc.Crypto.Picnic; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// Picnic asymmetric private key + /// + public sealed record class AsymmetricPicnicPrivateKey + : BouncyCastleAsymmetricPqcPrivateSignatureKeyBase< + AsymmetricPicnicPublicKey, + AsymmetricPicnicAlgorithm, + PicnicPublicKeyParameters, + PicnicPrivateKeyParameters, + PicnicSigner, + AsymmetricPicnicPrivateKey + > + { + /// + /// Constructor + /// + public AsymmetricPicnicPrivateKey() : base(AsymmetricPicnicAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricPicnicPrivateKey(byte[] keyData) : base(AsymmetricPicnicAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Keys + public AsymmetricPicnicPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricPicnicAlgorithm.ALGORITHM_NAME, keys) { } + + /// + new public static bool IsBcImportExportImplemented => false; + + /// + protected override byte[] SerializeKeyData() => SerializeFullKeyData(); + + /// + protected override void DeserializeKeyData() => DeserializeFullKeyData(); + + /// + protected override PicnicPublicKeyParameters GetPublicKey(PicnicPrivateKeyParameters privateKey) => throw new NotSupportedException(); + + /// + 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 AsymmetricPicnicPublicKey(AsymmetricPicnicPrivateKey privateKey) => privateKey.PublicKey; + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricPicnicPrivateKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricPicnicPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricPicnicPublicKey.cs new file mode 100644 index 0000000..e03b0e5 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricPicnicPublicKey.cs @@ -0,0 +1,56 @@ +using Org.BouncyCastle.Pqc.Crypto.Picnic; + +namespace wan24.Crypto.BC +{ + /// + /// Picnic asymmetric public key + /// + public sealed record class AsymmetricPicnicPublicKey + : BouncyCastleAsymmetricPqcPublicSignatureKeyBase + { + /// + /// Constructor + /// + public AsymmetricPicnicPublicKey() : base(AsymmetricPicnicAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricPicnicPublicKey(byte[] keyData) : base(AsymmetricPicnicAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Public key + public AsymmetricPicnicPublicKey(PicnicPublicKeyParameters publicKey) : base(AsymmetricPicnicAlgorithm.ALGORITHM_NAME, publicKey) { } + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + if (_PublicKey is null) throw new InvalidOperationException(); + return AsymmetricPicnicHelper.GetKeySize(_PublicKey.Parameters); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricPicnicPublicKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricSNtruPrimeAlgorithm.cs b/src/wan24-Crypto-BC/AsymmetricSNtruPrimeAlgorithm.cs new file mode 100644 index 0000000..4a29e1e --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricSNtruPrimeAlgorithm.cs @@ -0,0 +1,96 @@ +using Org.BouncyCastle.Pqc.Crypto.NtruPrime; +using Org.BouncyCastle.Security; +using System.Collections.ObjectModel; +using wan24.Core; + +namespace wan24.Crypto.BC +{ + /// + /// Streamlined NTRU Prime asymmetric algorithm + /// + public sealed record class AsymmetricSNtruPrimeAlgorithm + : BouncyCastleAsymmetricAlgorithmBase< + AsymmetricSNtruPrimePublicKey, + AsymmetricSNtruPrimePrivateKey, + SNtruPrimeKeyGenerationParameters, + SNtruPrimeParameters, + SNtruPrimePublicKeyParameters, + SNtruPrimePrivateKeyParameters, + AsymmetricSNtruPrimeAlgorithm + > + { + /// + /// Algorithm name + /// + public const string ALGORITHM_NAME = "SNTRUP"; + /// + /// Algorithm value + /// + public const int ALGORITHM_VALUE = 14; + /// + /// Default key size in bits + /// + public const int DEFAULT_KEY_SIZE = 1277; + /// + /// Algorithm usages + /// + public const AsymmetricAlgorithmUsages USAGES = AsymmetricAlgorithmUsages.KeyExchange; + /// + /// Display name + /// + public const string DISPLAY_NAME = "Streamlined NTRU Prime"; + + /// + /// Allowed key sizes in bits + /// + private static readonly ReadOnlyCollection _AllowedKeySizes; + + /// + /// Static constructor + /// + static AsymmetricSNtruPrimeAlgorithm() => _AllowedKeySizes = new List() + { + 653,// 128 bit security + 761,// 153 bit security + 857,// 175 bit security + 953,// 196 bit security + 1013,// 209 bit security + 1277// 270 bit security + }.AsReadOnly(); + + /// + /// Constructor + /// + public AsymmetricSNtruPrimeAlgorithm() + : base(ALGORITHM_NAME, ALGORITHM_VALUE, USAGES, isEllipticCurveAlgorithm: false, _AllowedKeySizes, isPostQuantum: true, DEFAULT_KEY_SIZE) + { } + + /// + public override string DisplayName => DISPLAY_NAME; + + /// + public override AsymmetricSNtruPrimePrivateKey CreateKeyPair(CryptoOptions? options = null) + { + try + { + options ??= DefaultOptions; + if (!options.AsymmetricKeyBits.In(AllowedKeySizes)) throw new ArgumentException("Invalid key size", nameof(options)); + SNtruPrimeKeyPairGenerator keyGen = new(); + keyGen.Init(CreateKeyGenParameters(new SecureRandom(BouncyCastleRandomGenerator.Instance()), GetEngineParameters(options), options)); + return Activator.CreateInstance(typeof(AsymmetricSNtruPrimePrivateKey), keyGen.GenerateKeyPair()) as AsymmetricSNtruPrimePrivateKey + ?? throw new InvalidProgramException($"Failed to instance asymmetric private key {typeof(AsymmetricSNtruPrimePrivateKey)}"); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + protected override SNtruPrimeParameters GetEngineParameters(CryptoOptions options) => AsymmetricSNtruPrimeHelper.GetParameters(options.AsymmetricKeyBits); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricSNtruPrimeHelper.cs b/src/wan24-Crypto-BC/AsymmetricSNtruPrimeHelper.cs new file mode 100644 index 0000000..02549b0 --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricSNtruPrimeHelper.cs @@ -0,0 +1,44 @@ +using Org.BouncyCastle.Pqc.Crypto.NtruPrime; + +//TODO PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo doesn't support SNtruPrimaPrivateKeyParameters !? (waiting for a fix and an update of the NuGet package at present) + +namespace wan24.Crypto.BC +{ + /// + /// Streamlined NTRU Prime helper + /// + public static class AsymmetricSNtruPrimeHelper + { + /// + /// Get the key size in bits + /// + /// Parameters + /// Key size in bits + public static int GetKeySize(this SNtruPrimeParameters param) + { + if (param == SNtruPrimeParameters.sntrup653) return 653; + if (param == SNtruPrimeParameters.sntrup761) return 761; + if (param == SNtruPrimeParameters.sntrup857) return 857; + if (param == SNtruPrimeParameters.sntrup953) return 953; + if (param == SNtruPrimeParameters.sntrup1013) return 1013; + if (param == SNtruPrimeParameters.sntrup1277) return 1277; + throw new ArgumentException("Invalid Streamlined NTRU Prime parameters", nameof(param)); + } + + /// + /// Get the Streamline NTRU Prime parameters + /// + /// Key size in bits + /// Parameters + public static SNtruPrimeParameters GetParameters(int keySize) => keySize switch + { + 653 => SNtruPrimeParameters.sntrup653, + 761 => SNtruPrimeParameters.sntrup761, + 857 => SNtruPrimeParameters.sntrup857, + 953 => SNtruPrimeParameters.sntrup953, + 1013 => SNtruPrimeParameters.sntrup1013, + 1277 => SNtruPrimeParameters.sntrup1277, + _ => throw new ArgumentException("Invalid key size", nameof(keySize)) + }; + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricSNtruPrimePrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricSNtruPrimePrivateKey.cs new file mode 100644 index 0000000..8b4869a --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricSNtruPrimePrivateKey.cs @@ -0,0 +1,134 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Pqc.Crypto.NtruPrime; +using wan24.Core; +using wan24.StreamSerializerExtensions; + +namespace wan24.Crypto.BC +{ + /// + /// Streamlined NTRU Prime asymmetric private key + /// + public sealed record class AsymmetricSNtruPrimePrivateKey + : BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase< + AsymmetricSNtruPrimePublicKey, + AsymmetricSNtruPrimeAlgorithm, + SNtruPrimePublicKeyParameters, + SNtruPrimePrivateKeyParameters, + SNtruPrimeKemGenerator, + SNtruPrimeKemExtractor, + AsymmetricSNtruPrimePrivateKey + > + { + /// + /// Constructor + /// + public AsymmetricSNtruPrimePrivateKey() : base(AsymmetricSNtruPrimeAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricSNtruPrimePrivateKey(byte[] keyData) : base(AsymmetricSNtruPrimeAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Keys + public AsymmetricSNtruPrimePrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricSNtruPrimeAlgorithm.ALGORITHM_NAME, keys) { } + + /// + new public static bool IsBcImportExportImplemented => false; + + /// + 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 SNtruPrimePrivateKeyParameters)!.GetPrivateKey()); + using SecureByteArray publicKey = new((Keys.Public as SNtruPrimePublicKeyParameters)!.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(); + SNtruPrimePrivateKeyParameters? privateKey = null; + SNtruPrimePublicKeyParameters? publicKey = null; + try + { + using MemoryStream ms = new(KeyData.Array); + int ssv = ms.ReadSerializerVersion(); + SNtruPrimeParameters param = AsymmetricSNtruPrimeHelper.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 SNtruPrimePublicKeyParameters GetPublicKey(SNtruPrimePrivateKeyParameters privateKey) => throw new NotSupportedException(); + + /// + 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 AsymmetricSNtruPrimePublicKey(AsymmetricSNtruPrimePrivateKey privateKey) => privateKey.PublicKey; + + /// + /// Cast from serialized data + /// + /// Data + public static explicit operator AsymmetricSNtruPrimePrivateKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricSNtruPrimePublicKey.cs b/src/wan24-Crypto-BC/AsymmetricSNtruPrimePublicKey.cs new file mode 100644 index 0000000..dbf7a4d --- /dev/null +++ b/src/wan24-Crypto-BC/AsymmetricSNtruPrimePublicKey.cs @@ -0,0 +1,109 @@ +using Org.BouncyCastle.Pqc.Crypto.NtruPrime; +using wan24.Core; +using wan24.StreamSerializerExtensions; + +namespace wan24.Crypto.BC +{ + /// + /// Streamlined NTRU Prime asymmetric public key + /// + public sealed record class AsymmetricSNtruPrimePublicKey + : BouncyCastleAsymmetricPqcPublicKeyBase + { + /// + /// Constructor + /// + public AsymmetricSNtruPrimePublicKey() : base(AsymmetricSNtruPrimeAlgorithm.ALGORITHM_NAME) { } + + /// + /// Constructor + /// + /// Key data + public AsymmetricSNtruPrimePublicKey(byte[] keyData) : base(AsymmetricSNtruPrimeAlgorithm.ALGORITHM_NAME, keyData) { } + + /// + /// Constructor + /// + /// Public key + public AsymmetricSNtruPrimePublicKey(SNtruPrimePublicKeyParameters publicKey) : base(AsymmetricSNtruPrimeAlgorithm.ALGORITHM_NAME, publicKey) { } + + /// + new public static bool IsBcImportExportImplemented => false; + + /// + public override int Bits + { + get + { + try + { + EnsureUndisposed(); + if (_PublicKey is null) throw new InvalidOperationException(); + return AsymmetricSNtruPrimeHelper.GetKeySize(_PublicKey.Parameters); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + 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(); + SNtruPrimeParameters param = AsymmetricSNtruPrimeHelper.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 + /// + /// Data + public static explicit operator AsymmetricSNtruPrimePublicKey(byte[] data) => Import(data); + } +} diff --git a/src/wan24-Crypto-BC/AsymmetricSphincsPlusAlgorithm.cs b/src/wan24-Crypto-BC/AsymmetricSphincsPlusAlgorithm.cs index deba181..6ee2cb2 100644 --- a/src/wan24-Crypto-BC/AsymmetricSphincsPlusAlgorithm.cs +++ b/src/wan24-Crypto-BC/AsymmetricSphincsPlusAlgorithm.cs @@ -49,9 +49,9 @@ public sealed record class AsymmetricSphincsPlusAlgorithm /// static AsymmetricSphincsPlusAlgorithm() => _AllowedKeySizes = new List() { - 128, - 192, - 256 + 128,// 128 bit security + 192,// 192 bit security + 256// 256 bit security }.AsReadOnly(); /// diff --git a/src/wan24-Crypto-BC/AsymmetricSphincsPlusHelper.cs b/src/wan24-Crypto-BC/AsymmetricSphincsPlusHelper.cs index ba45ecd..dd2e66c 100644 --- a/src/wan24-Crypto-BC/AsymmetricSphincsPlusHelper.cs +++ b/src/wan24-Crypto-BC/AsymmetricSphincsPlusHelper.cs @@ -1,5 +1,8 @@ using Org.BouncyCastle.Pqc.Crypto.SphincsPlus; + +//FIXME Switch to SPHINCS+ Shake parameters, 'cause they've been approved in the FIPS standard + namespace wan24.Crypto.BC { /// diff --git a/src/wan24-Crypto-BC/AsymmetricSphincsPlusPrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricSphincsPlusPrivateKey.cs index 6d534f5..1f112d9 100644 --- a/src/wan24-Crypto-BC/AsymmetricSphincsPlusPrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricSphincsPlusPrivateKey.cs @@ -36,6 +36,12 @@ public AsymmetricSphincsPlusPrivateKey(byte[] keyData) : base(AsymmetricSphincsP /// Keys public AsymmetricSphincsPlusPrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricSphincsPlusAlgorithm.ALGORITHM_NAME, keys) { } + /// + /// Constructor + /// + /// Private key + public AsymmetricSphincsPlusPrivateKey(SphincsPlusPrivateKeyParameters privateKey) : base(AsymmetricSphincsPlusAlgorithm.ALGORITHM_NAME, privateKey) { } + /// protected override SphincsPlusPublicKeyParameters GetPublicKey(SphincsPlusPrivateKeyParameters privateKey) => new(privateKey.Parameters, privateKey.GetPublicKey()); @@ -55,6 +61,7 @@ protected override byte[] SerializeKeyData() ms.WriteSerializerVersion() .WriteNumber(Bits) .WriteBytes(privateKey.Array) + //TODO Don't include SPHINCS+ public key in serialized data .WriteBytes(publicKey.Array); return ms.ToArray(); } @@ -81,7 +88,8 @@ protected override void DeserializeKeyData() using MemoryStream ms = new(KeyData.Array); int ssv = ms.ReadSerializerVersion(); SphincsPlusParameters param = AsymmetricSphincsPlusHelper.GetParameters(ms.ReadNumber(ssv)); - privateKey = new(param, ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); + using SecureByteArrayRefStruct privateKeyInfo = new(ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); + privateKey = new(param, privateKeyInfo.Array); publicKey = new(param, ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); Keys = new(publicKey, privateKey); } diff --git a/src/wan24-Crypto-BC/AsymmetricSphincsPlusPublicKey.cs b/src/wan24-Crypto-BC/AsymmetricSphincsPlusPublicKey.cs index 95008b9..bcb9d03 100644 --- a/src/wan24-Crypto-BC/AsymmetricSphincsPlusPublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricSphincsPlusPublicKey.cs @@ -35,7 +35,8 @@ public override int Bits try { EnsureUndisposed(); - return _PublicKey?.Parameters.GetKeySize() ?? throw new InvalidOperationException(); + if (_PublicKey is null) throw new InvalidOperationException(); + return AsymmetricSphincsPlusHelper.GetKeySize(_PublicKey.Parameters); } catch (CryptographicException) { diff --git a/src/wan24-Crypto-BC/AsymmetricX25519Algorithm.cs b/src/wan24-Crypto-BC/AsymmetricX25519Algorithm.cs index 9f7b40d..de72243 100644 --- a/src/wan24-Crypto-BC/AsymmetricX25519Algorithm.cs +++ b/src/wan24-Crypto-BC/AsymmetricX25519Algorithm.cs @@ -8,7 +8,7 @@ namespace wan24.Crypto.BC { /// - /// X25519 asymmetric algorithm + /// X25519 asymmetric algorithm (128 bit security)) /// public sealed record class AsymmetricX25519Algorithm : BouncyCastleAsymmetricAlgorithmBase< diff --git a/src/wan24-Crypto-BC/AsymmetricX25519PrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricX25519PrivateKey.cs index 32145fc..19579f2 100644 --- a/src/wan24-Crypto-BC/AsymmetricX25519PrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricX25519PrivateKey.cs @@ -34,6 +34,12 @@ public AsymmetricX25519PrivateKey(byte[] keyData) : base(AsymmetricX25519Algorit /// Keys public AsymmetricX25519PrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricX25519Algorithm.ALGORITHM_NAME, keys) { } + /// + /// Constructor + /// + /// Private key + public AsymmetricX25519PrivateKey(X25519PrivateKeyParameters privateKey) : base(AsymmetricX25519Algorithm.ALGORITHM_NAME, privateKey) { } + /// public override (byte[] Key, byte[] KeyExchangeData) GetKeyExchangeData(IAsymmetricPublicKey? publicKey = null, CryptoOptions? options = null) { diff --git a/src/wan24-Crypto-BC/AsymmetricX448Algorithm.cs b/src/wan24-Crypto-BC/AsymmetricX448Algorithm.cs index 1caf7b5..45d65a1 100644 --- a/src/wan24-Crypto-BC/AsymmetricX448Algorithm.cs +++ b/src/wan24-Crypto-BC/AsymmetricX448Algorithm.cs @@ -8,7 +8,7 @@ namespace wan24.Crypto.BC { /// - /// X448 asymmetric algorithm + /// X448 asymmetric algorithm (224 bit security)) /// public sealed record class AsymmetricX448Algorithm : BouncyCastleAsymmetricAlgorithmBase< diff --git a/src/wan24-Crypto-BC/AsymmetricX448PrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricX448PrivateKey.cs index 529a3cb..aa9c58f 100644 --- a/src/wan24-Crypto-BC/AsymmetricX448PrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricX448PrivateKey.cs @@ -34,6 +34,12 @@ public AsymmetricX448PrivateKey(byte[] keyData) : base(AsymmetricX448Algorithm.A /// Keys public AsymmetricX448PrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricX448Algorithm.ALGORITHM_NAME, keys) { } + /// + /// Constructor + /// + /// Private key + public AsymmetricX448PrivateKey(X448PrivateKeyParameters privateKey) : base(AsymmetricX448Algorithm.ALGORITHM_NAME, privateKey) { } + /// public override int Bits { diff --git a/src/wan24-Crypto-BC/AsymmetricXEd25519Algorithm.cs b/src/wan24-Crypto-BC/AsymmetricXEd25519Algorithm.cs index a818b91..98ef2f8 100644 --- a/src/wan24-Crypto-BC/AsymmetricXEd25519Algorithm.cs +++ b/src/wan24-Crypto-BC/AsymmetricXEd25519Algorithm.cs @@ -8,7 +8,7 @@ namespace wan24.Crypto.BC { /// - /// XEd25519 asymmetric algorithm (converts the used Ed25519 private key to a X25519 private key for key exchange) + /// XEd25519 asymmetric algorithm (converts the used Ed25519 private key to a X25519 private key for key exchange, 128 bit security) /// public sealed record class AsymmetricXEd25519Algorithm : BouncyCastleAsymmetricAlgorithmBase< diff --git a/src/wan24-Crypto-BC/AsymmetricXEd25519PrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricXEd25519PrivateKey.cs index 8d0ef44..399e5fc 100644 --- a/src/wan24-Crypto-BC/AsymmetricXEd25519PrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricXEd25519PrivateKey.cs @@ -40,6 +40,12 @@ public AsymmetricXEd25519PrivateKey(byte[] keyData) : base(AsymmetricXEd25519Alg /// Keys public AsymmetricXEd25519PrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricXEd25519Algorithm.ALGORITHM_NAME, keys) { } + /// + /// Constructor + /// + /// Private key + public AsymmetricXEd25519PrivateKey(Ed25519PrivateKeyParameters privateKey) : base(AsymmetricXEd25519Algorithm.ALGORITHM_NAME, privateKey) { } + /// public override AsymmetricXEd25519PublicKey PublicKey { @@ -49,8 +55,7 @@ public override AsymmetricXEd25519PublicKey PublicKey { EnsureUndisposed(); if (Keys is null) throw new InvalidOperationException(); - return _PublicKey ??= Activator.CreateInstance(typeof(AsymmetricXEd25519PublicKey), Keys.Public, GetX25519Key().PublicKey) as AsymmetricXEd25519PublicKey - ?? throw new InvalidProgramException($"Failed to instance {typeof(AsymmetricXEd25519PublicKey)}"); + return _PublicKey ??= new((Keys.Public as Ed25519PublicKeyParameters)!, GetX25519Key().PublicKey); } catch (CryptographicException) { @@ -130,6 +135,7 @@ protected override async Task DisposeCore() private AsymmetricX25519PrivateKey GetX25519Key() { EnsureUndisposed(); + if (X25519Key is not null) return X25519Key; if (Keys?.Private is not Ed25519PrivateKeyParameters privateKey) throw new InvalidOperationException(); X25519PrivateKeyParameters pk = privateKey.ToX25519PrivateKey(); return X25519Key = new(new AsymmetricCipherKeyPair(pk.GeneratePublicKey(), pk)); diff --git a/src/wan24-Crypto-BC/AsymmetricXEd25519PublicKey.cs b/src/wan24-Crypto-BC/AsymmetricXEd25519PublicKey.cs index e2f2e37..28995b8 100644 --- a/src/wan24-Crypto-BC/AsymmetricXEd25519PublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricXEd25519PublicKey.cs @@ -39,6 +39,9 @@ public AsymmetricXEd25519PublicKey(Ed25519PublicKeyParameters publicKey, Asymmet KeyData = new(SerializeKeyData()); } + /// + new public static bool IsBcImportExportImplemented => false; + /// public override int Bits { diff --git a/src/wan24-Crypto-BC/AsymmetricXEd448Algorithm.cs b/src/wan24-Crypto-BC/AsymmetricXEd448Algorithm.cs index b559f8c..952e1b5 100644 --- a/src/wan24-Crypto-BC/AsymmetricXEd448Algorithm.cs +++ b/src/wan24-Crypto-BC/AsymmetricXEd448Algorithm.cs @@ -8,7 +8,7 @@ namespace wan24.Crypto.BC { /// - /// XEd448 asymmetric algorithm (converts the used Ed448 private key to a X448 private key for key exchange) + /// XEd448 asymmetric algorithm (converts the used Ed448 private key to a X448 private key for key exchange; 224 bit security) /// public sealed record class AsymmetricXEd448Algorithm : BouncyCastleAsymmetricAlgorithmBase< diff --git a/src/wan24-Crypto-BC/AsymmetricXEd448PrivateKey.cs b/src/wan24-Crypto-BC/AsymmetricXEd448PrivateKey.cs index cf3076e..4b6fc49 100644 --- a/src/wan24-Crypto-BC/AsymmetricXEd448PrivateKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricXEd448PrivateKey.cs @@ -40,6 +40,12 @@ public AsymmetricXEd448PrivateKey(byte[] keyData) : base(AsymmetricXEd448Algorit /// Keys public AsymmetricXEd448PrivateKey(AsymmetricCipherKeyPair keys) : base(AsymmetricXEd448Algorithm.ALGORITHM_NAME, keys) { } + /// + /// Constructor + /// + /// Private key + public AsymmetricXEd448PrivateKey(Ed448PrivateKeyParameters privateKey) : base(AsymmetricXEd448Algorithm.ALGORITHM_NAME, privateKey) { } + /// public override AsymmetricXEd448PublicKey PublicKey { @@ -49,8 +55,7 @@ public override AsymmetricXEd448PublicKey PublicKey { EnsureUndisposed(); if (Keys is null) throw new InvalidOperationException(); - return _PublicKey ??= Activator.CreateInstance(typeof(AsymmetricXEd448PublicKey), Keys.Public, GetX448Key().PublicKey) as AsymmetricXEd448PublicKey - ?? throw new InvalidProgramException($"Failed to instance {typeof(AsymmetricXEd448PublicKey)}"); + return _PublicKey ??= new((Keys.Public as Ed448PublicKeyParameters)!, GetX448Key().PublicKey); } catch (CryptographicException) { @@ -139,6 +144,7 @@ protected override async Task DisposeCore() private AsymmetricX448PrivateKey GetX448Key() { EnsureUndisposed(); + if (X448Key is not null) return X448Key; if (Keys?.Private is not Ed448PrivateKeyParameters privateKey) throw new InvalidOperationException(); X448PrivateKeyParameters pk = privateKey.ToX448PrivateKey(); return X448Key = new(new AsymmetricCipherKeyPair(pk.GeneratePublicKey(), pk)); diff --git a/src/wan24-Crypto-BC/AsymmetricXEd448PublicKey.cs b/src/wan24-Crypto-BC/AsymmetricXEd448PublicKey.cs index d31bb97..f360aa2 100644 --- a/src/wan24-Crypto-BC/AsymmetricXEd448PublicKey.cs +++ b/src/wan24-Crypto-BC/AsymmetricXEd448PublicKey.cs @@ -45,6 +45,9 @@ public AsymmetricXEd448PublicKey(Ed448PublicKeyParameters publicKey, AsymmetricX /// Public key public AsymmetricXEd448PublicKey(Ed448PublicKeyParameters publicKey) : base(AsymmetricXEd448Algorithm.ALGORITHM_NAME, publicKey) { } + /// + new public static bool IsBcImportExportImplemented => false; + /// public override int Bits { diff --git a/src/wan24-Crypto-BC/BcEllipticCurves.cs b/src/wan24-Crypto-BC/BcEllipticCurves.cs index 8607ab9..d51842b 100644 --- a/src/wan24-Crypto-BC/BcEllipticCurves.cs +++ b/src/wan24-Crypto-BC/BcEllipticCurves.cs @@ -9,15 +9,15 @@ namespace wan24.Crypto.BC public static class BcEllipticCurves { /// - /// secp256r1 curve (NIST P-256) + /// secp256r1 curve (NIST P-256, 128 bit security) /// public static readonly ECDomainParameters SECP256R1_CURVE = new(SecNamedCurves.GetByOid(SecObjectIdentifiers.SecP256r1)); /// - /// secp384r1 curve (NIST P-384) + /// secp384r1 curve (NIST P-384, 192 bit security) /// public static readonly ECDomainParameters SECP384R1_CURVE = new(SecNamedCurves.GetByOid(SecObjectIdentifiers.SecP384r1)); /// - /// secp521r1 curve (NIST P-521) + /// secp521r1 curve (NIST P-521, 260 bit security) /// public static readonly ECDomainParameters SECP521R1_CURVE = new(SecNamedCurves.GetByOid(SecObjectIdentifiers.SecP521r1)); diff --git a/src/wan24-Crypto-BC/Bootstrap.cs b/src/wan24-Crypto-BC/Bootstrap.cs index 8f9fea3..59d6948 100644 --- a/src/wan24-Crypto-BC/Bootstrap.cs +++ b/src/wan24-Crypto-BC/Bootstrap.cs @@ -1,5 +1,7 @@ using wan24.Core; +//TODO Use SNTRUP as default key exchange algorithm in a newer version + [assembly: Bootstrapper(typeof(wan24.Crypto.BC.Bootstrap), nameof(wan24.Crypto.BC.Bootstrap.Boot))] namespace wan24.Crypto.BC @@ -19,9 +21,7 @@ public static void Boot() AsymmetricHelper.Algorithms[AsymmetricFrodoKemAlgorithm.ALGORITHM_NAME] = AsymmetricFrodoKemAlgorithm.Instance; AsymmetricHelper.Algorithms[AsymmetricDilithiumAlgorithm.ALGORITHM_NAME] = AsymmetricDilithiumAlgorithm.Instance; AsymmetricHelper.Algorithms[AsymmetricFalconAlgorithm.ALGORITHM_NAME] = AsymmetricFalconAlgorithm.Instance; - //FIXME PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo fails with Frodo*KeyParameters !? (waiting for an update of the NuGet package at present) 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[AsymmetricEd25519Algorithm.ALGORITHM_NAME] = AsymmetricEd25519Algorithm.Instance; AsymmetricHelper.Algorithms[AsymmetricEd448Algorithm.ALGORITHM_NAME] = AsymmetricEd448Algorithm.Instance; @@ -29,6 +29,10 @@ public static void Boot() AsymmetricHelper.Algorithms[AsymmetricX448Algorithm.ALGORITHM_NAME] = AsymmetricX448Algorithm.Instance; AsymmetricHelper.Algorithms[AsymmetricXEd25519Algorithm.ALGORITHM_NAME] = AsymmetricXEd25519Algorithm.Instance; AsymmetricHelper.Algorithms[AsymmetricXEd448Algorithm.ALGORITHM_NAME] = AsymmetricXEd448Algorithm.Instance; + AsymmetricHelper.Algorithms[AsymmetricSNtruPrimeAlgorithm.ALGORITHM_NAME] = AsymmetricSNtruPrimeAlgorithm.Instance; + AsymmetricHelper.Algorithms[AsymmetricBikeAlgorithm.ALGORITHM_NAME] = AsymmetricBikeAlgorithm.Instance; + AsymmetricHelper.Algorithms[AsymmetricHqcAlgorithm.ALGORITHM_NAME] = AsymmetricHqcAlgorithm.Instance; + AsymmetricHelper.Algorithms[AsymmetricPicnicAlgorithm.ALGORITHM_NAME] = AsymmetricPicnicAlgorithm.Instance; // ChaCha20 EncryptionHelper.Algorithms[EncryptionChaCha20Algorithm.ALGORITHM_NAME] = EncryptionChaCha20Algorithm.Instance; CryptoProfiles.Registered[EncryptionChaCha20Algorithm.PROFILE_CHACHA20_RAW] = new CryptoOptions() diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricAlgorithmBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricAlgorithmBase.cs index faa1b80..4d94f43 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricAlgorithmBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricAlgorithmBase.cs @@ -18,7 +18,7 @@ namespace wan24.Crypto.BC /// Private key type /// Final type public abstract record class BouncyCastleAsymmetricAlgorithmBase - : AsymmetricAlgorithmBase + : BouncyCastleAsymmetricAlgorithmBase where tPublic : BouncyCastleAsymmetricPublicKeyBase, new() where tPrivate : BouncyCastleAsymmetricPrivateKeyBase, new() where tKeyGen : IAsymmetricCipherKeyPairGenerator, new() @@ -26,6 +26,70 @@ public abstract record class BouncyCastleAsymmetricAlgorithmBase, new() + { + /// + /// Constructor + /// + /// Algorithm name + /// Algorithm value + /// Algorithm usages + /// Is an elliptic curve algorithm? + /// Allowed key sizes in bits + /// Is a post quantum-safe algorithm? + /// Default key size in bits + protected BouncyCastleAsymmetricAlgorithmBase( + string name, + int value, + AsymmetricAlgorithmUsages usages, + bool isEllipticCurveAlgorithm, + ReadOnlyCollection allowedKeySizes, + bool isPostQuantum, + int defaultKeySize + ) + : base(name, value, usages, isEllipticCurveAlgorithm, allowedKeySizes, isPostQuantum, defaultKeySize) + { } + + /// + public override tPrivate CreateKeyPair(CryptoOptions? options = null) + { + try + { + options ??= DefaultOptions; + 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)); + return Activator.CreateInstance(typeof(tPrivate), keyGen.GenerateKeyPair()) as tPrivate + ?? throw new InvalidProgramException($"Failed to instance asymmetric private key {typeof(tPrivate)}"); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + } + + /// + /// Base class for a Bouncy Castle asymmetric algorithm + /// + /// Public key type + /// Private key type + /// Key generator parameters type + /// Key parameters type + /// Public key type + /// Private key type + /// Final type + public abstract record class BouncyCastleAsymmetricAlgorithmBase + : AsymmetricAlgorithmBase + where tPublic : BouncyCastleAsymmetricPublicKeyBase, new() + where tPrivate : BouncyCastleAsymmetricPrivateKeyBase, new() + where tKeyGenParam : KeyGenerationParameters + where tPublicKey : AsymmetricKeyParameter, ICipherParameters + where tPrivateKey : AsymmetricKeyParameter + where tFinal : BouncyCastleAsymmetricAlgorithmBase, new() { /// /// Static constructor @@ -77,28 +141,6 @@ int defaultKeySize /// public sealed override bool IsPostQuantum { get; } - /// - public override tPrivate CreateKeyPair(CryptoOptions? options = null) - { - try - { - options ??= DefaultOptions; - 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)); - return Activator.CreateInstance(typeof(tPrivate), keyGen.GenerateKeyPair()) as tPrivate - ?? throw new InvalidProgramException($"Failed to instance asymmetric private key {typeof(tPrivate)}"); - } - catch (CryptographicException) - { - throw; - } - catch (Exception ex) - { - throw CryptographicException.From(ex); - } - } - /// public override tPrivate DeserializePrivateKeyV1(byte[] keyData) => throw new NotSupportedException(); diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateKeyBase.cs index 8820b61..9c37e07 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateKeyBase.cs @@ -43,6 +43,13 @@ protected BouncyCastleAsymmetricNonPqcPrivateKeyBase(string algorithm, byte[] ke /// Keys protected BouncyCastleAsymmetricNonPqcPrivateKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + /// + /// Constructor + /// + /// Algorithm name + /// Private key + protected BouncyCastleAsymmetricNonPqcPrivateKeyBase(string algorithm, tPrivateKey privateKey) : base(algorithm, privateKey) { } + /// protected override byte[] SerializeKeyData() { diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase.cs index 11d8f3b..9f91c25 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase.cs @@ -40,6 +40,13 @@ protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase(string algorithm, /// Keys protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + /// + /// Constructor + /// + /// Algorithm name + /// Private key + protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase(string algorithm, tPrivateKey privateKey) : base(algorithm, privateKey) { } + /// public override byte[] SignHashRaw(byte[] hash) { diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2.cs index 64817a1..e7654fe 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2.cs @@ -40,6 +40,13 @@ protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2(string algorithm, /// Keys protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + /// + /// Constructor + /// + /// Algorithm name + /// Private key + protected BouncyCastleAsymmetricNonPqcPrivateSignatureKeyBase2(string algorithm, tPrivateKey privateKey) : base(algorithm, privateKey) { } + /// public override byte[] SignHashRaw(byte[] hash) { diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyBase.cs index 72a825e..8093df1 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyBase.cs @@ -40,6 +40,28 @@ protected BouncyCastleAsymmetricPqcPrivateKeyBase(string algorithm, byte[] keyDa /// Keys protected BouncyCastleAsymmetricPqcPrivateKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + /// + /// Constructor + /// + /// Algorithm name + /// Private key + protected BouncyCastleAsymmetricPqcPrivateKeyBase(string algorithm, tPrivateKey privateKey) : base(algorithm, privateKey) { } + + /// + public override byte[] ExportBc() + { + try + { + EnsureUndisposed(); + if (Keys is null) throw new InvalidOperationException(); + return PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo(Keys.Private).GetDerEncoded(); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + /// protected override byte[] SerializeKeyData() { @@ -96,14 +118,16 @@ protected override byte[] SerializeFullKeyData() try { EnsureUndisposed(); - if (Keys == null) throw new InvalidOperationException(); + if (Keys is null) throw new InvalidOperationException(); using MemoryPoolStream ms = new() { CleanReturned = true }; + using SecureByteArrayRefStruct privateKey = new(PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo(Keys.Private).GetDerEncoded()); + using SecureByteArrayRefStruct publicKey = new(PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(Keys.Public).GetDerEncoded()); ms.WriteSerializerVersion() - .WriteBytes(PqcPrivateKeyInfoFactory.CreatePrivateKeyInfo(Keys.Private).GetDerEncoded()) - .WriteBytes(PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(Keys.Public).GetDerEncoded()); + .WriteBytes(privateKey) + .WriteBytes(publicKey); return ms.ToArray(); } catch (CryptographicException) @@ -128,8 +152,10 @@ protected override void DeserializeFullKeyData() { 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); + using SecureByteArrayRefStruct privateKeyInfo = new(ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); + using SecureByteArrayRefStruct publicKeyInfo = new(ms.ReadBytes(ssv, maxLen: ushort.MaxValue).Value); + privateKey = (tPrivateKey)PqcPrivateKeyFactory.CreateKey(privateKeyInfo); + publicKey = (tPublicKey)PqcPublicKeyFactory.CreateKey(publicKeyInfo); Keys = new(publicKey, privateKey); } catch @@ -148,5 +174,20 @@ protected override void DeserializeFullKeyData() throw CryptographicException.From(ex); } } + + /// + new public static tFinal ImportBc(in byte[] keyInfo) + { + try + { + return (tFinal)(Activator.CreateInstance(typeof(tFinal), PqcPrivateKeyFactory.CreateKey(keyInfo) as tPrivateKey + ?? throw new InvalidDataException($"Failed to deserialize {typeof(tPrivateKey)} from the given key data")) + ?? throw new InvalidProgramException($"Failed to instance {typeof(tFinal)}")); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } } } diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase.cs index bab4229..988a1ff 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase.cs @@ -44,6 +44,13 @@ protected BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase(string algorithm, b /// Keys protected BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + /// + /// Constructor + /// + /// Algorithm name + /// Private key + protected BouncyCastleAsymmetricPqcPrivateKeyExchangeKeyBase(string algorithm, tPrivateKey privateKey) : base(algorithm, privateKey) { } + /// public override (byte[] Key, byte[] KeyExchangeData) GetKeyExchangeData(IAsymmetricPublicKey? publicKey = null, CryptoOptions? options = null) { diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateSignatureKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateSignatureKeyBase.cs index 6ffdb1a..3be7754 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateSignatureKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPrivateSignatureKeyBase.cs @@ -41,6 +41,13 @@ protected BouncyCastleAsymmetricPqcPrivateSignatureKeyBase(string algorithm, byt /// Keys protected BouncyCastleAsymmetricPqcPrivateSignatureKeyBase(string algorithm, AsymmetricCipherKeyPair keys) : base(algorithm, keys) { } + /// + /// Constructor + /// + /// Algorithm name + /// Private key + protected BouncyCastleAsymmetricPqcPrivateSignatureKeyBase(string algorithm, tPrivateKey privateKey) : base(algorithm, privateKey) { } + /// public sealed override byte[] SignHashRaw(byte[] hash) { diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPublicKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPublicKeyBase.cs index ccdc373..1969b4e 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPublicKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPqcPublicKeyBase.cs @@ -34,6 +34,21 @@ protected BouncyCastleAsymmetricPqcPublicKeyBase(string algorithm, byte[] keyDat /// Public key protected BouncyCastleAsymmetricPqcPublicKeyBase(string algorithm, tPublicKey publicKey) : base(algorithm, publicKey) { } + /// + public override byte[] ExportBc() + { + try + { + EnsureUndisposed(); + if (_PublicKey is null) throw new InvalidOperationException(); + return PqcSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(_PublicKey).GetDerEncoded(); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + /// protected override byte[] SerializeKeyData() { @@ -70,5 +85,20 @@ protected override void DeserializeKeyData() throw CryptographicException.From(ex); } } + + /// + new public static tFinal ImportBc(in byte[] keyInfo) + { + try + { + return (tFinal)(Activator.CreateInstance(typeof(tFinal), PqcPublicKeyFactory.CreateKey(keyInfo) as tPublicKey + ?? throw new InvalidDataException()) + ?? throw new InvalidProgramException($"Failed to instance {typeof(tFinal)}")); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } } } diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateKeyBase.cs index e00f002..218b9c4 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPrivateKeyBase.cs @@ -1,4 +1,6 @@ using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; namespace wan24.Crypto.BC { @@ -48,9 +50,9 @@ protected BouncyCastleAsymmetricPrivateKeyBase(string algorithm, AsymmetricCiphe { try { - Keys = keys; if (keys.Public is not tPublicKey) throw new ArgumentException("No valid public key parameters", nameof(keys)); if (keys.Private is not tPrivateKey) throw new ArgumentException("No valid private key parameters", nameof(keys)); + Keys = keys; KeyData = new(SerializeKeyData()); } catch (CryptographicException) @@ -63,6 +65,33 @@ protected BouncyCastleAsymmetricPrivateKeyBase(string algorithm, AsymmetricCiphe } } + /// + /// Constructor + /// + /// Algorithm name + /// Private key + protected BouncyCastleAsymmetricPrivateKeyBase(string algorithm, tPrivateKey privateKey) : this(algorithm) + { + try + { + Keys = new(GetPublicKey(privateKey), privateKey); + KeyData = new(SerializeKeyData()); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + + /// + /// Is the key info export/import implemented in the Bouncy Castle library AND wan24-Crypto-BC? + /// + public static bool IsBcImportExportImplemented { get; } = true; + /// /// Private key /// @@ -113,6 +142,25 @@ public override tPublic PublicKey /// public override int Bits => PublicKey.Bits; + /// + /// Export the key in Bouncy Castle format, if possible + /// + /// Serialized key data (DER encoded; don't forget to clear!) + public virtual byte[] ExportBc() + { + try + { + EnsureUndisposed(); + if (!IsBcImportExportImplemented) throw new NotSupportedException(); + if (Keys is null) throw new InvalidOperationException(); + return PrivateKeyInfoFactory.CreatePrivateKeyInfo(Keys.Private).GetDerEncoded(); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } + /// /// Serialize the key data /// @@ -141,5 +189,25 @@ public override tPublic PublicKey /// Private key /// Public key protected abstract tPublicKey GetPublicKey(tPrivateKey privateKey); + + /// + /// Import a key in Bouncy Castle format (created by ) + /// + /// Serialized key data (created by ; won't be cleared) + /// Key (don't forget to dispose!) + public static tFinal ImportBc(in byte[] keyInfo) + { + try + { + if (!IsBcImportExportImplemented) throw new NotSupportedException(); + return (tFinal)(Activator.CreateInstance(typeof(tFinal), PrivateKeyFactory.CreateKey(keyInfo) as tPrivateKey + ?? throw new InvalidDataException($"Failed to deserialize {typeof(tPrivateKey)} from the given key data")) + ?? throw new InvalidProgramException($"Failed to instance {typeof(tFinal)}")); + } + catch (Exception ex) + { + throw CryptographicException.From(ex); + } + } } } diff --git a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPublicKeyBase.cs b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPublicKeyBase.cs index c2a5855..930ad13 100644 --- a/src/wan24-Crypto-BC/BouncyCastleAsymmetricPublicKeyBase.cs +++ b/src/wan24-Crypto-BC/BouncyCastleAsymmetricPublicKeyBase.cs @@ -1,4 +1,7 @@ using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; +using wan24.Core; namespace wan24.Crypto.BC { @@ -57,6 +60,11 @@ protected BouncyCastleAsymmetricPublicKeyBase(string algorithm, tPublicKey publi } } + /// + /// Is the key info export/import implemented in the Bouncy Castle library AND wan24-Crypto-BC? + /// + public static bool IsBcImportExportImplemented { get; } = true; + /// /// Public key /// @@ -81,13 +89,32 @@ public tPublicKey PublicKey } } + /// + /// Export the key in Bouncy Castle format, if possible + /// + /// Serialized key data (DER encoded; don't forget to clear!) + public virtual byte[] ExportBc() + { + try + { + EnsureUndisposed(); + if (!IsBcImportExportImplemented) throw new NotSupportedException(); + if (_PublicKey is null) throw new InvalidOperationException(); + return SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(_PublicKey).GetDerEncoded(); + } + catch(Exception ex) + { + throw CryptographicException.From(ex); + } + } + /// public sealed override IAsymmetricPublicKey GetCopy() { try { EnsureUndisposed(); - return Activator.CreateInstance(typeof(tFinal), (byte[])KeyData.Array.Clone()) as IAsymmetricPublicKey + return Activator.CreateInstance(typeof(tFinal), KeyData.Array.CloneArray()) as IAsymmetricPublicKey ?? throw new InvalidProgramException($"Failed to instance {typeof(tFinal)}"); } catch (Exception ex) @@ -106,5 +133,25 @@ public sealed override IAsymmetricPublicKey GetCopy() /// Deserialize the key data /// protected abstract void DeserializeKeyData(); + + /// + /// Import a key in Bouncy Castle format (created by ) + /// + /// Serialized key data (created by ; won't be cleared) + /// Key (don't forget to dispose!) + public static tFinal ImportBc(in byte[] keyInfo) + { + try + { + if (!IsBcImportExportImplemented) throw new NotSupportedException(); + return (tFinal)(Activator.CreateInstance(typeof(tFinal), PublicKeyFactory.CreateKey(keyInfo) as tPublicKey + ?? throw new InvalidDataException()) + ?? throw new InvalidProgramException($"Failed to instance {typeof(tFinal)}")); + } + catch(Exception ex) + { + throw CryptographicException.From(ex); + } + } } } diff --git a/src/wan24-Crypto-BC/README.md b/src/wan24-Crypto-BC/README.md index 5d91cef..4b73b52 100644 --- a/src/wan24-Crypto-BC/README.md +++ b/src/wan24-Crypto-BC/README.md @@ -12,14 +12,18 @@ the `wan24-Crypto` library with these algorithms: | CRYSTALS-Dilithium | 3 | CRYSTALSDILITHIUM | | FALCON | 4 | FALCON | | SPHINCS+ | 5 | SPHINCSPLUS | -| FrodoKEM* | 6 | FRODOKEM | -| NTRUEncrypt* | 7 | NTRUENCRYPT | +| FrodoKEM | 6 | FRODOKEM | +| NTRUEncrypt | 7 | NTRUENCRYPT | | Ed25519 | 8 | ED25519 | | Ed448 | 9 | ED448 | | X25519 | 10 | X25519 | | X448 | 11 | X448 | | XEd25519 | 12 | XED25519 | | XEd448 | 13 | XED448 | +| Streamlined NTRU Prime | 14 | SNTRUP | +| BIKE | 15 | BIKE | +| HQC | 16 | HQC | +| Picnic | 17 | PICNIC | | **Symmetric** | | | | ChaCha20 | 1 | CHACHA20 | | XSalsa20 | 2 | XSALSA20 | @@ -29,14 +33,10 @@ the `wan24-Crypto` library with these algorithms: | Twofish 256 CBC (ISO10126 padding) | 7 | TWOFISH256CBC | | Twofish 256 GCM AEAD (128 bit MAC) | 8 | TWOFISH256GCM | -NTRUSign is currently not implemented, 'cause it'd require the using code to -be GPL licensed. This algorithm may be included in a separate package which is -licensed using the GPL license (to avoid misunderstandings) in the future. - -**NOTE**: SPHINCS+ and NTRUEncrypt key serialization uses a custom serializer -at present, which will change in the future, as soon as Bouncy Castle -implemented a (working) serializer (again). This change will require a manual -key conversion from the current serialization format. +Main goals of this extension library are to make `wan24-Crypto` usable on all +platforms and extend its algorithms by PQC algorithms and other non-PQC +algorithms, which are not available from .NET, but implemented in the Bouncy +Castle library. ## How to get it @@ -54,21 +54,7 @@ wan24.Crypto.BC.Bootstrap.Boot(); This will register the algorithms to the `wan24-Crypto` library. -To set Bouncy Castle defaults as `wan24-Crypto` defaults: - -```cs -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 +### `wan24-Crypto` algorithm replacement 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: @@ -95,7 +81,28 @@ 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. +instead. The `NetShake128/256HashAlgorithmAdapter` can't be replaced for this +reason. + +### Use as default algorithms + +To set Bouncy Castle defaults as `wan24-Crypto` defaults: + +```cs +BouncyCastle.SetDefaults(); +``` + +Per default the current `wan24-Crypto` default will be set as counter +algorithms to `HybridAlgorithmHelper`. + +Current Bouncy Castle default algorithms are: + +| Usage | Algorithm | +| ----- | --------- | +| Key exchange | NTRUEncrypt | +| Signature | CRYSTALS-Dilithium | +| Encryption | Serpent 256 bit CBC | +| PAKE encryption | Serpent 256 bit GCM | ## Post quantum safety @@ -107,6 +114,10 @@ These asymmetric algorithms are designed for post quantum cryptography: - SPHINCS+ (signature) - FrodoKEM (key exchange) - NTRUEncrypt (key exchange) +- Streamlined NTRU Prime (key exchange) +- BIKE (key exchange) +- HQC (key exchange) +- Picnic (signature) 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: @@ -148,11 +159,23 @@ SignatureContainer signature = dataToSign.Sign(yourNormalPrivateKey, options: op ## Algorithm parameters used -For CRYSTALS-Kyber and CRYSTALS-Dilithium the non-AES parameters are being -used, since the AES parameter sets have been deprecated. When using SPHINCS+, -the Haraka simple hashing parameters will be used (since the Haraka robust -hashing parameters have been reprecated). For FrodoKEM the AES parameters will -be used. +| Algorithm | Parameters | +| --------- | ---------- | +| CRYSTALS-Kyber, CRYSTALS-Dilithium | non-AES | +| SPHINCS+ | Haraka simple* | +| FrodoKEM | AES* | +| Picnic | Full | + +**NOTE**: CRYSTALS-Kyber and CRYSTALS-Dilithium AES parameters and SPHINCS+ +robust parameters are deprecated! SPHINCS+ Haraka parameters are removed from +the FIPS standard, so `wan24-Crypto-BC` will switch to Shake parameters +instead. Also the FrodoKEM Shake parameters will be used in the next major +release, which will require to renew existing keys, which use the AES +parameters from the current version of this library. + +**WARNING** The PQC standards are in development at the moment, so future +incompatible changes are very likely and will be handled in a new major +release of this library. ## Random data provider @@ -186,8 +209,10 @@ bytes of an underlaying PRNG using a random key. The result is a CSRNG. These stream ciphers are available with `wan24-Crypto-BC`, but you could use any other stream cipher (but not AEAD implementations!) also: -- ChaCha20 - `ChaCha20Rng` -- XSalsa20 - `XSalsa20Rng` +| Stream cipher | RNG | +| ------------- | --- | +| ChaCha20 | `ChaCha20Rng` | +| XSalsa20 | `XSalsa20Rng` | If you didn't specify an underlaying PRNG, Bouncy Castle's `VmpcRandomGenerator` will be used and seeded using 256 bytes from `RND`. diff --git a/src/wan24-Crypto-BC/wan24-Crypto-BC.csproj b/src/wan24-Crypto-BC/wan24-Crypto-BC.csproj index da8803d..7763557 100644 --- a/src/wan24-Crypto-BC/wan24-Crypto-BC.csproj +++ b/src/wan24-Crypto-BC/wan24-Crypto-BC.csproj @@ -9,7 +9,7 @@ True wan24-Crypto-BC wan24-Crypto-BC - 3.0.0 + 3.1.0 nd1012 Andreas Zimmermann, wan24.de wan24-Crypto-BC @@ -33,8 +33,8 @@ - - + +