diff --git a/.github/actions/setup-build-agent/action.yml b/.github/actions/setup-build-agent/action.yml index 75289697373..fdc8f7fb353 100644 --- a/.github/actions/setup-build-agent/action.yml +++ b/.github/actions/setup-build-agent/action.yml @@ -35,12 +35,13 @@ runs: run: print("::warning ::No compiler cache available, build times might suffer") shell: python if: env.COMPILER_CACHE_LOCATION == '' && inputs.cache-key != '' - - uses: actions/cache@v3 + - uses: actions/cache@v4 if: env.COMPILER_CACHE_LOCATION != '' && inputs.cache-key != '' with: path: ${{ env.COMPILER_CACHE_LOCATION }} key: ${{ inputs.cache-key }}-${{ github.run_id }} restore-keys: ${{ inputs.cache-key }} + save-always: true - name: Setup Visual Studio Environment uses: egor-tensin/vs-shell@v2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8248efcbf7..d5d734202ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: runs-on: ${{ matrix.host_os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Build Agent uses: ./.github/actions/setup-build-agent @@ -77,7 +77,7 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Build Agent uses: ./.github/actions/setup-build-agent @@ -103,7 +103,7 @@ jobs: runs-on: macos-13 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Build Agent uses: ./.github/actions/setup-build-agent @@ -118,7 +118,7 @@ jobs: name: "Clang Tidy" runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Build Agent uses: ./.github/actions/setup-build-agent @@ -177,12 +177,12 @@ jobs: COVERALLS_REPO_TOKEN: pbLoTMBxC1DFvbws9WfrzVOvfEdEZTcCS steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: ./source - name: Fetch BoringSSL fork for BoGo tests - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: randombit/boringssl ref: rene/runner-20230705 @@ -221,7 +221,7 @@ jobs: runs-on: ${{ matrix.host_os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: ./source @@ -274,7 +274,7 @@ jobs: host_os: macos-13 - target: emscripten compiler: emcc - host_os: macos-13 + host_os: macos-14 runs-on: ${{ matrix.host_os }} @@ -282,7 +282,7 @@ jobs: ANDROID_NDK: android-ndk-r26 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Build Agent uses: ./.github/actions/setup-build-agent diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 552a36a4457..101d7d77db8 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -22,7 +22,7 @@ jobs: fuzz-seconds: 180 dry-run: false - name: Upload Crash - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9d060684e59..fe6e0d69789 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Build Agent uses: ./.github/actions/setup-build-agent @@ -56,7 +56,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 78cf9d0d622..7267510fd7b 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Build Agent uses: ./.github/actions/setup-build-agent @@ -51,7 +51,7 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Build Agent uses: ./.github/actions/setup-build-agent @@ -68,7 +68,7 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Build Agent uses: ./.github/actions/setup-build-agent @@ -86,7 +86,7 @@ jobs: steps: - name: Fetch Botan Repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Build Agent uses: ./.github/actions/setup-build-agent @@ -101,7 +101,7 @@ jobs: --test-target server --parallel $(nproc) - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: tls-anvil-server-test-results path: | diff --git a/doc/api_ref/contents.rst b/doc/api_ref/contents.rst index f747c8d9c1f..f15f5771bf8 100644 --- a/doc/api_ref/contents.rst +++ b/doc/api_ref/contents.rst @@ -31,6 +31,7 @@ API Reference tss ecc compression + providers pkcs11 tpm otp diff --git a/doc/api_ref/providers.rst b/doc/api_ref/providers.rst new file mode 100644 index 00000000000..0ea4683184f --- /dev/null +++ b/doc/api_ref/providers.rst @@ -0,0 +1,169 @@ +External Providers +============================== + +Botan ships with a variety of cryptographic algorithms in both pure software +as well as with support from :doc:`hardware acceleration <../hardware_acceleration>`. + +Additionally, Botan allows to use external implementations to provide algorithms ("providers"). + +Integrated Providers +------------------------------ + +PKCS#11 +^^^^^^^^^^^^^ + +PKCS#11 is a standard API for accessing cryptographic hardware. Botan +ships a :doc:`PKCS#11 provider ` for interacting with PKCS#11 +devices which provide cryptographic algorithms. It is enabled by default. + +TPM 1.2 +^^^^^^^^^^^^^ + +The TPM 1.2 standard is a specification for a hardware device which provides +cryptographic algorithms. Botan ships a :doc:`TPM provider ` for interacting +with TPM devices. It is disabled by default. + +CommonCrypto +^^^^^^^^^^^^^ + +CommonCrypto is a library provided by Apple for accessing cryptographic +algorithms. Botan ships a *CommonCrypto* provider for interacting with CommonCrypto. +It is disabled by default. + +The CommonCrypto provider supports the following algorithms: + +* SHA-1, SHA-256, SHA-384, SHA-512 +* AES-128, AES-192, AES-256, DES, TDES, Blowfish, CAST-128 +* CBC, CTR, OFB + +Provider Interfaces +------------------------------ + +Symmetric Algorithms +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following interfaces can be used to implement providers +for symmetric algorithms: + +* ``AEAD_Mode`` +* ``BlockCipher`` +* ``Cipher_Mode`` +* ``Hash`` +* ``KDF`` +* ``MAC`` +* ``PasswordHashFamily`` +* ``PBKDF`` +* ``StreamCipher`` +* ``XOF`` + +Each of the interfaces provide a factory method which takes string arguments +and returns an object implementing the interface. The strings are the name of +the algorithm to be instantiated and the provider to be used. +For example, the following code creates a SHA-256 hash object using the +*CommonCrypto* provider: + +.. code-block:: cpp + + #include + + auto hash = Botan::HashFunction::create_or_throw("SHA-256", "CommonCrypto"); + + hash->update("Hello"); + hash->update(" "); + hash->update("World"); + auto digest = hash->final(); + + // query the provider currently used + std::string provider = hash->provider(); // "CommonCrypto" + +Omitting the provider string or leaving it empty means the default provider +is used. The default provider is the first provider which supports the +requested algorithm. Depending on how Botan was configured at build time, +the default provider may be a pure software implementation, a hardware +accelerated implementation or an implementation using an integrated provider, +e.g., CommonCrypto. + +The following rules apply: + +1. If Botan was built with an integrated provider that is hooked into the + ``T::create()``/``T::create_or_throw()`` factory methods (currently only *CommonCrypto* is), + the default provider is the integrated provider. + +2. If Botan was not built with an integrated provider as in (1), but + with hardware acceleration support, e.g., AES-NI, and the hardware acceleration + is available at runtime, the default provider is the hardware accelerated provider. + +3. If Botan was not built with an integrated provider as in (1) and not built + with hardware acceleration support, the default provider is the pure software + implementation. + +Regardless of the default provider, a specific provider can always be requested +by passing the provider name as the second argument to ``T::create()``/``T::create_or_throw()``. +Specifically, the special provider name ``"base"`` can always be used to +request the hardware accelerated (preferred, if available at runtime) +or pure software implementation (last fallback). + +Public Key Algorithms +^^^^^^^^^^^^^^^^^^^^^^^ + +The following interfaces support using providers for +:doc:`public key algorithms `. The interfaces are used +in a similar way as the interfaces for symmetric algorithms +described above. + +* ``PK_Signer`` +* ``PK_Verifier`` +* ``PK_Key_Agreement`` +* ``PK_Encryptor_EME`` +* ``PK_Decryptor_EME`` +* ``PK_KEM_Encryptor`` +* ``PK_KEM_Decryptor`` + +Each of the interfaces provides a constructor which takes a key object, +optional parameters, and a string specifying the provider to be used. +For example, the following code signs a message using an RSA key with the +*CommonCrypto* provider: + +.. note:: No integrated provider currently supports using any public key algorithm + in the way described above, so the example is purely for illustrative purposes. + +.. code:: cpp + + #include + #include + #include + + Botan::AutoSeeded_RNG rng; + auto key = Botan::create_private_key("RSA", rng, "3072"); + + Botan::PK_Signer signer(key, rng, "EMSA3(SHA-256)", Botan::Signature_Format::Standard, "CommonCrypto"); + + signer.update("Hello"); + signer.update(" "); + signer.update("World"); + auto signature = signer.signature(rng); + +To create a key object, use ``Botan::create_private_key()``, which takes +a string specifying the algorithm and the provider to be used. For example, to +create a 3072 bit RSA key with the *CommonCrypto* provider: + +.. note:: No integrated provider currently supports creating any private key + in the way described above, so the example is purely for illustrative purposes. + +.. code:: cpp + + #include + #include + + Botan::AutoSeeded_RNG rng; + + auto key = Botan::create_private_key("RSA", rng, "3072", "CommonCrypto"); + +Another way to implement a provider for public key algorithms is to implement +the ``Private_Key`` and ``Public_Key`` interfaces. This allows for different +use cases, e.g., to use a key stored in a hardware security module, handled +by a different operating system process (to avoid leaking the key material), +or even implement an algorithm not supported by Botan. The resulting key class +can be stored outside Botan and still be used with the ``PK_Signer``, +``PK_Verifier``, ``PK_Key_Agreement``, ``PK_Encryptor_EME``, ``PK_Decryptor_EME``, +``PK_KEM_Encryptor``, and ``PK_KEM_Decryptor`` interfaces. diff --git a/doc/api_ref/tls.rst b/doc/api_ref/tls.rst index c10c7fc591c..1f1606a468a 100644 --- a/doc/api_ref/tls.rst +++ b/doc/api_ref/tls.rst @@ -1317,14 +1317,28 @@ The asio Stream offers the following interface: .. _https_client_example: -Code Example: HTTPS Client -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Code Examples: HTTPS Client using Boost Beast +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The code below illustrates how to build a simple HTTPS client based on the TLS Stream and Boost.Beast. When run, it fetches the content of `https://botan.randombit.net/news.html` and prints it to stdout. +Starting with Botan 3.3.0 (and assuming a recent version of Boost), one may use +Botan's TLS using C++20 coroutines. The following example implements a simple +HTTPS client that may be used to fetch content from web servers: + +.. literalinclude:: /../src/examples/tls_stream_coroutine_client.cpp + :language: cpp + +Of course, the ASIO stream may also be used in a more traditional way, using +callback handler methods instead of coroutines: .. literalinclude:: /../src/examples/tls_stream_client.cpp :language: cpp +For some websites this might not work and report a "bad_certificate". Botan's +default TLS policy requires that the server sends a valid CRL or OCSP response. +Some servers don't do that and thus the certificate is rejected. To disable this +check, derive the default policy and override `require_cert_revocation_info()` +accordingly. + .. _tls_session_encryption: TLS Session Encryption diff --git a/doc/credits.rst b/doc/credits.rst index 4a1f7f40580..c951e2a3b8f 100644 --- a/doc/credits.rst +++ b/doc/credits.rst @@ -163,7 +163,7 @@ snail-mail address (S), and Bitcoin address (B). N: René Meusel E: rene.meusel@rohde-schwarz.com W: https://www.rohde-schwarz.com/cybersecurity - D: CI, TLS 1.3, Kyber, Dilithium, SPHINCS+ + D: CI, TLS 1.3, Kyber, Dilithium, SPHINCS+, FrodoKEM S: Berlin, Germany N: Philippe Lieser diff --git a/doc/migration_guide.rst b/doc/migration_guide.rst index ce742a738da..c2a4c75dfda 100644 --- a/doc/migration_guide.rst +++ b/doc/migration_guide.rst @@ -26,6 +26,14 @@ algorithm headers (such as ``aes.h``) have been removed. Instead you should create objects via the factory methods (in the case of AES, ``BlockCipher::create``) which works in both 2.x and 3.0 +Errata: ``pk_ops.h`` +^^^^^^^^^^^^^^^^^^^^ + +Between Botan 3.0 and 3.2 the public header ``pk_ops.h`` was removed +accidentally. This header is typically required for specialized applications +that interface with dedicated crypto hardware. If you are migrating such an +application, please make sure to use Botan 3.3 or newer. + Build Artifacts --------------- diff --git a/doc/security.rst b/doc/security.rst index 11ed81dbdcf..4f5b1295232 100644 --- a/doc/security.rst +++ b/doc/security.rst @@ -15,6 +15,29 @@ mail please use:: This key can be found in the file ``doc/pgpkey.txt`` or online at https://keybase.io/jacklloyd and on most PGP keyservers. +2024 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* 2024-02-20: Kyber side channel + + The Kyber implementation was vulnerable to the KyberSlash1 and + KyberSlash2 side channel issues. + + Introduced in 3.0.0, fixed in 3.3.0 + +* 2024-02-20: DoS due to oversized elliptic curve parameters + + When decoding an ASN.1 encoded elliptic curve, Botan would verify the `p` + parameter was actually prime, and at least some minimum size. However it + failed to check if the prime was far too large (for example thousands of + bits), in which case checking the prime would take a significant amount of + computation. Now the maximum size of arbitrary elliptic curves when decoding + from ASN.1 is limited. + + Reported by Bing Shi + + Fixed in 3.3.0 and 2.19.4 + 2022 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/news.rst b/news.rst index 83a5efbe750..2fc4324fb66 100644 --- a/news.rst +++ b/news.rst @@ -1,14 +1,96 @@ Release Notes ======================================== -Version 3.3.0, Not Yet Released +Version 3.3.0, 2024-02-20 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* Fix a potential denial of service caused by accepting arbitrary + length primes as potential elliptic curve parameters in ASN.1 + encodings. With very large inputs the primality verification + can become computationally expensive. Now any prime field larger + than 1024 bits is rejected immediately. Reported by Bing Shi. + (GH #3913) + +* Add FrodoKEM post-quantum KEM (GH #3679 #3807 #3892) + +* Add support for Blake2s (GH #3796) + +* Add support for RFC 7250 in TLS 1.3 to allow authenticating peers + using raw public keys (GH #3771) + +* Update the BSI TLS policy to match the latest TR, particularly + enabling support for TLS 1.3 (GH #3809) + +* Add AsymmetricKey::generate_another() to generate a new key of the + same type and parameters as an existing key (GH #3770 #3786) + +* Add Private_Key::remaining_operations() that indicates the number of + remaining signatures for stateful hash-based signatures (GH #3821) + +* Add implementation of EC_PrivateKey::check_key() (GH #3782 #3804) + +* Add hardware acceleration for SHA-512 on ARMv8 (GH #3860 #3864) + +* X.509 certificates that contain Authority Information Access (AIA) + extensions can now be encoded (GH #3784) + * Various functions defined in ``mem_ops.h`` are now deprecated - for public use (GH #3759 #3757 #3755) + for public use (GH #3759 #3752 #3757) -* Use ``BufferStuffer`` and ``concat`` helpers in public key code - (GH #3756 #3753) +* The ASIO TLS stream can now be used with C++20 coroutines (GH #3764) + +* New public header asio_compat.h to check compatibility of the ASIO + TLS stream with the available boost version (1.73.0+) (GH #3765) + +* Flatten input buffer sequences in the ASIO TLS stream to avoid + creating unnecessarily small TLS records (GH #3839) + +* Hard-rename the potentially harmful build configuration flag + --terminate-on-asserts to --unsafe-terminate-on-asserts (GH #3755) + +* Use modern SQLite3 APIs with integer width annotations from SQLite3 3.37 + (GH #3788 #3793) + +* Generate and install a CMake package config file (botan-config.cmake) + (GH #3722 #3827 #3830 #3825) + +* Add TLS::Channel::is_handshake_complete() predicate method (GH #3762) + +* Add support for setting thread names on Haiku OS and DragonflyBSD + (GH #3758 #3785) + +* Use /Zc:throwingNew with MSVC (GH #3858) + +* Work around a warning in GCC 13 (GH #3852) + +* Add a CLI utility for testing RSA side channels using the MARVIN + toolkit (GH #3749) + +* CLI utility 'tls_http_server' is now based on Boost Beast + (GH #3763 #3791) + +* CLI utility 'tls_client_hello' can detect and handle TLS 1.3 messages + (GH #3820) + +* Add a detailed migration guide for users of OpenSSL 1.1 (GH #3815) + +* Various updates to the documentation and code examples + (GH #3777 #3805 #3802 #3794 #3815 #3823 #3828 #3842 #3841 #3849 #3745) + +* Fixes and improvements to the build experience using ``ninja`` + (GH #3751 #3750 #3769 #3798 #3848) + +* Fix handling of cofactors when performing scalar blinding in EC (GH #3803) + +* Fix potential timing side channels in Kyber (GH #3846 #3874) + +* Fix a potential dangling reference resulting in a crash in the OCB + mode of operation (GH #3814) + +* Fix validity checks in the construction of the ASIO TLS stream + (GH #3766) + +* Fix error code handling in ASIO TLS stream (GH #3795 #3801 #3773) * Fix a TLS 1.3 assertion failure that would trigger if the application callback returned an empty certificate chain. (GH #3754) @@ -17,12 +99,40 @@ Version 3.3.0, Not Yet Released server would fail to reject a client hello that advertised (only) FFDHE groups that are not known to us. (GH #3743 #3742 #3729) -* Add a cli utility for testing RSA side channels using the MARVIN - toolkit (GH #3749) +* Fix that modifications made in TLS::Callbacks::tls_modify_extensions() + for the TLS 1.3 Certificate message were not being applied. (GH #3792) + +* Fix string mapping of the PKCS#11 mechanism RSA signing mechanism that + use SHA-384 (GH #3868) + +* Fix a build issue on NetBSD (GH #3767) + +* Fix the configure.py to avoid recursing out of our source tree (GH #3748) + +* Fix various clang-tidy warnings (GH #3822) + +* Fix CLI tests on windows and enable them in CI (GH #3845) + +* Use ``BufferStuffer`` and ``concat`` helpers in public key code + (GH #3756 #3753) + +* Add a nightly test to ensure hybrid TLS 1.3 PQ/T compatibility with + external implementations (GH #3740) + +* Internal memory operation helpers are now memory container agnostic + using C++20 ranges (GH #3715 #3707) + +* Public and internal headers are now clearly separated in the build + directory. That restricts the examples build target to public headers. + (GH #3880) -* Add support for setting thread names on Haiku OS (GH #3758) +* House keeping for better code formatting with clang-format + (GH #3862 #3865) -* Fix a build problem using ``ninja`` (GH #3751 #3750) +* Build documentation in CI and fail on warnings or errors (GH #3838) + +* Work around a GitHub Actions CI issue (actions/runner-images#8659) + (GH #3783 #3833 #3888) Version 3.2.0, 2023-10-09 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -335,6 +445,50 @@ Other Improvements * Fix bugs in GMAC and SipHash where they would require a fresh key be provided for each message. (GH #2908) +Version 2.19.4, 2024-02-20 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Fix a potential denial of service caused by accepting arbitrary + length primes as potential elliptic curve parameters in ASN.1 + encodings. With very large inputs the primality verification + can become computationally expensive. Now any prime field larger + than 1024 bits is rejected immediately. Reported by Bing Shi. + (GH #3914) + +* Switch to using a constant time binary algorithm for computing + GCD (GH #3912) + +* Fix a bug in SHAKE_Cipher which could cause incorrect output + if set_key was called multiple times. (GH #3192) + +* Fix a bug in RSA-KEM encryption where the shared secret key + was incorrectly not padded to exactly the byte length of the + modulus. This would cause an incorrect shared key with ~1/256 + probability. (GH #3380) + +* In RSA decryption and signature verification, reject bytestrings + which are longer than the public modulus. Previously, otherwise + valid signatures/ciphertexts with additional leading zero bytes + would also be accepted. (GH #3380) + +* Add support for short nonces in XTS (GH #3384 #3336) + +* Fix NIST keywrap which was incorrect when wrapping 64-bit keys + (GH #3384 #3340) + +* Fix nonce handling bug in EAX (GH #3382 #3335) + +* Fix a bug in PKCS11 AttributeContainer where adding an attribute + that already existed could cause incorrect references to the + existing attributes. (GH #3185) + +* Apply patches which allow GCC 4.7 to compile Botan 2.x. Previously + at least GCC 4.8 had been required. (GH #3273) + +* Fix a build time problem affecting VCpkg (GH #3071) + +* Fix a build problem affecting Windows ARM with Visual C++ (GH #3871) + Version 2.19.3, 2022-11-16 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/readme.rst b/readme.rst index 81a26c76596..32fafd43abc 100644 --- a/readme.rst +++ b/readme.rst @@ -55,14 +55,14 @@ Releases ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The latest release from the Botan2 release series is -`2.19.3 `_ -`(sig) `__, -released on 2022-11-16. +`2.19.4 `_ +`(sig) `__, +released on 2024-02-20. The latest release from the Botan3 release series is -`3.2.0 `_ -`(sig) `__, -released on 2023-10-09. +`3.3.0 `_ +`(sig) `__, +released on 2024-02-20. All releases are signed with a `PGP key `_. See the `release notes `_ for @@ -101,7 +101,7 @@ Public Key Cryptography * DH and ECDH key agreement * Signature schemes ECDSA, DSA, Ed25519, ECGDSA, ECKCDSA, SM2, GOST 34.10 * Post-quantum signature schemes Dilithium, SPHINCS+, and XMSS -* Post-quantum key agreement schemes McEliece and Kyber +* Post-quantum key agreement schemes McEliece, Kyber and FrodoKEM * ElGamal encryption * Padding schemes OAEP, PSS, PKCS #1 v1.5, X9.31 diff --git a/src/build-data/detect_version.cpp b/src/build-data/detect_version.cpp index 34a9776ecd3..666102be089 100644 --- a/src/build-data/detect_version.cpp +++ b/src/build-data/detect_version.cpp @@ -22,6 +22,9 @@ XLC __open_xl_version__ __open_xl_release__ #elif defined(__EMSCRIPTEN__) + #if __has_include() + #include + #endif EMCC __EMSCRIPTEN_major__ __EMSCRIPTEN_minor__ #elif defined(__clang__) && defined(__apple_build_version__) diff --git a/src/examples/hybrid_key_encapsulation.cpp b/src/examples/hybrid_key_encapsulation.cpp new file mode 100644 index 00000000000..09fecae5ac5 --- /dev/null +++ b/src/examples/hybrid_key_encapsulation.cpp @@ -0,0 +1,377 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * This class is an example of a custom public-key algorithm in Botan. + * + * It combines a classic key exchange algorithm like Diffie-Hellman and a key + * encapsulation mechanism (KEM) to provide a "hybrid" key encapsulation + * mechanism (KEM). + * + * This approach is useful as an intermediate step towards post-quantum secure + * cryptography as it combines the historical confidence in a classic algorithm + * with the future-proofness of a post-quantum algorithm. + * + * Other use cases for such a custom public-key algorithm class include: + * - adding support for a new public-key algorithm that Botan doesn't support + * - writing a wrapper to offload public-key operations to a hardware device + */ +class Hybrid_PublicKey : public virtual Botan::Public_Key { + public: + explicit Hybrid_PublicKey(std::unique_ptr kex, std::unique_ptr kem) : + m_kex_pk(std::move(kex)), m_kem_pk(std::move(kem)) { + BOTAN_ASSERT_NONNULL(m_kex_pk); + BOTAN_ASSERT_NONNULL(m_kem_pk); + BOTAN_ASSERT_NOMSG(m_kex_pk->supports_operation(Botan::PublicKeyOperation::KeyAgreement)); + BOTAN_ASSERT_NOMSG(m_kem_pk->supports_operation(Botan::PublicKeyOperation::KeyEncapsulation)); + } + + std::string algo_name() const override { + return "Hybrid-KEM(" + m_kex_pk->algo_name() + "," + m_kem_pk->algo_name() + ")"; + } + + /** + * This returns an object of a custom sub-class of + * Botan::PK_Ops::KEM_Encryption. See below for the implementation of that + * class, where the actual hybrid operation is performed. + * + * Note, that applications typically don't call this directly, but they + * use the Botan::PK_KEM_Encryptor class, which in turn calls this method. + * See the main() function below for an example. + */ + std::unique_ptr create_kem_encryption_op(std::string_view params, + std::string_view provider) const override; + + /** + * In an actual implementation, when you want to use this key in a + * protocol like X.509, this may return an algorithm identifier that fits + * your needs. For instance, using a custom OID. + */ + Botan::AlgorithmIdentifier algorithm_identifier() const override { + throw Botan::Not_Implemented("Hybrid-KEM does not have an algorithm identifier"); + } + + /** + * In an actual implementation, this may return a serialized + * representation of the public keys. For instance, using some ASN.1 + * encoding to combine the two public keys. + */ + std::vector public_key_bits() const override { + throw Botan::Not_Implemented("Key serialization is not supported"); + } + + std::unique_ptr generate_another(Botan::RandomNumberGenerator& rng) const override; + + bool supports_operation(Botan::PublicKeyOperation op) const override { + return op == Botan::PublicKeyOperation::KeyEncapsulation; + } + + size_t estimated_strength() const override { + return std::max(m_kex_pk->estimated_strength(), m_kem_pk->estimated_strength()); + } + + size_t key_length() const override { return m_kex_pk->key_length() + m_kem_pk->key_length(); } + + bool check_key(Botan::RandomNumberGenerator& rng, bool strong) const override { + return m_kex_pk->check_key(rng, strong) && m_kem_pk->check_key(rng, strong); + } + + const Botan::Public_Key& kex_public_key() const { return *m_kex_pk; } + + const Botan::Public_Key& kem_public_key() const { return *m_kem_pk; } + + private: + std::unique_ptr m_kex_pk; + std::unique_ptr m_kem_pk; +}; + +/** + * This is the private key class for the custom public-key algorithm. + */ +class Hybrid_PrivateKey : public virtual Botan::Private_Key, + public virtual Hybrid_PublicKey { + public: + explicit Hybrid_PrivateKey(std::unique_ptr kex, std::unique_ptr kem) : + Hybrid_PublicKey(kex->public_key(), kem->public_key()), + m_kex_sk(std::move(kex)), + m_kem_sk(std::move(kem)) {} + + /** + * This returns an object of a custom sub-class of Botan::PK_Ops::KEM_Decryption. + * See below for the implementation of that class, where the actual hybrid operation + * is performed. + * + * Note, that applications typically don't call this directly, but they + * use the Botan::PK_KEM_Decryptor class, which in turn calls this method. + * See the main() function below for an example. + */ + std::unique_ptr create_kem_decryption_op(Botan::RandomNumberGenerator& rng, + std::string_view params, + std::string_view provider) const override; + + /** + * In an actual implementation, this should return a serialized + * representation of the private keys. For instance, using some ASN.1 + * encoding to combine the two private keys. + */ + Botan::secure_vector private_key_bits() const override { + throw Botan::Not_Implemented("Key serialization is not supported"); + } + + std::unique_ptr public_key() const override { + return std::make_unique(m_kex_sk->public_key(), m_kem_sk->public_key()); + } + + const Botan::Private_Key& kex_private_key() const { return *m_kex_sk; } + + const Botan::Private_Key& kem_private_key() const { return *m_kem_sk; } + + private: + std::unique_ptr m_kex_sk; + std::unique_ptr m_kem_sk; +}; + +namespace { + +/** + * This implements the actual hybrid key encapsulation operation. It derives + * shared secrets from the key exchange algorithm (KEX) and the key + * encapsulation mechanism (KEM), and combines them using a Key Derivation + * Function (KDF). + */ +class Hybrid_Encryption_Operation : public Botan::PK_Ops::KEM_Encryption { + public: + Hybrid_Encryption_Operation(const Hybrid_PublicKey& hybrid_pk, std::string_view kdf) : + m_hybrid_pk(hybrid_pk), + m_kem_encryptor(hybrid_pk.kem_public_key(), "Raw"), + m_kdf(Botan::KDF::create_or_throw(kdf)) { + BOTAN_ASSERT_NONNULL(m_kdf); + } + + /** + * This returns the length of the encapsulated key in bytes. For such a + * hybrid key encapsulation, this comprises the length of the KEX's public + * key (ephemeral key pair) and the length of the KEM's encapsulated key. + */ + size_t encapsulated_key_length() const override { + return m_hybrid_pk.kex_public_key().public_key_bits().size() + m_kem_encryptor.encapsulated_key_length(); + } + + /** + * This returns the length of the output shared secret in bytes. It is + * the output length of the KDF, which acts as the "combiner" of the + * shared secrets of both algorithms. + */ + size_t shared_key_length(size_t desired_shared_key_length) const override { return desired_shared_key_length; } + + /** + * This method performs the actual hybrid key encapsulation operation. + */ + void kem_encrypt(std::span out_encapsed_key, + std::span out_shared_key, + Botan::RandomNumberGenerator& rng, + size_t desired_shared_key_length, + std::span salt) override { + // The basic idea of the hybrid operation: + // 1. Generate an ephemeral key pair for the key exchange algorithm, + // 2. and agree on a shared secret using the KEX's public key of the + // other party and the ephemeral private key, + // 3. Encapsulate a shared secret using the KEM's public key of the + // other party, resulting in a shared secret and its encapsulation, + // 4. Concatenate the ephemeral public key and the encapsulation to + // form a "hybrid encapsulation" (to be sent to the other party), + // 5. Concatenate the shared secrets of both algorithms and pass the + // result through a user-defined key derivation function to form a + // "hybrid shared secret" (to be used by the application). + + // 1. KEX: Generate an ephemeral key pair with the same parameters as + // the provided key exchange public key. + auto ephemeral_keypair = m_hybrid_pk.kex_public_key().generate_another(rng); + + // Note: Currently, we cannot pre-create the PK_Key_Agreement object in + // the constructor, because it requires an RNG object. + // + // TODO: fix this upstream by harmonizing the constructors of the + // PK_Key_Agreement and PK_KEM_Encryptor classes. + Botan::PK_Key_Agreement kex(*ephemeral_keypair, rng, "Raw"); + + // 2. KEX: Agree on a shared secret using the public key of the other + // party and our ephemeral private key. The ephemeral public + // key acts as the "encapsulation" of the key agreement. + // + // Note: kex.derive_key() does not have a std::span<> based overload to + // write straight into the output buffer. + // + // TODO: kex.derive_key() should allow a std::span<>-based out param, + // which would save a copy in this case. (See GH #3318) + const auto kex_shared_key = + kex.derive_key(0 /* no KDF */, m_hybrid_pk.kex_public_key().public_key_bits()).bits_of(); + const auto kex_encapsed_key = ephemeral_keypair->public_key_bits(); + + // 3. KEX: Encapsulate a shared secret using the KEM's public key, + // yielding a shared secret and its encapsulation. + const auto [kem_encapsed_key, kem_shared_key] = + Botan::KEM_Encapsulation::destructure(m_kem_encryptor.encrypt(rng)); + + // 4. Hybrid: Concatenate the ephemeral public key and the KEM's + // encapsulation to form a combined "hybrid encapsulation". + BOTAN_ASSERT_NOMSG(out_encapsed_key.size() == kex_encapsed_key.size() + kem_encapsed_key.size()); + std::copy(kex_encapsed_key.begin(), kex_encapsed_key.end(), out_encapsed_key.begin()); + std::copy( + kem_encapsed_key.begin(), kem_encapsed_key.end(), out_encapsed_key.begin() + kex_encapsed_key.size()); + + // 5. Hybrid: Combine the shared secrets of both algorithms. + Botan::secure_vector concat_shared_key; + concat_shared_key.insert(concat_shared_key.end(), kex_shared_key.begin(), kex_shared_key.end()); + concat_shared_key.insert(concat_shared_key.end(), kem_shared_key.begin(), kem_shared_key.end()); + + BOTAN_ASSERT_NOMSG(out_shared_key.size() >= desired_shared_key_length); + m_kdf->derive_key(out_shared_key.first(desired_shared_key_length), concat_shared_key, salt, {}); + } + + private: + const Hybrid_PublicKey& m_hybrid_pk; + Botan::PK_KEM_Encryptor m_kem_encryptor; + const std::unique_ptr m_kdf; +}; + +/** + * This implements the actual hybrid key decapsulation operation. It derives + * the shared secrets from the key exchange algorithm (KEX) and the key + * encapsulation mechanism (KEM), and combines them using a Key Derivation + * Function (KDF). + */ +class Hybrid_Decryption_Operation : public Botan::PK_Ops::KEM_Decryption { + public: + Hybrid_Decryption_Operation(const Hybrid_PrivateKey& hybrid_sk, + Botan::RandomNumberGenerator& rng, + std::string_view kdf) : + m_hybrid_sk(hybrid_sk), + m_key_agreement(hybrid_sk.kex_private_key(), rng, "Raw"), + m_kem_decryptor(hybrid_sk.kem_private_key(), rng, "Raw"), + m_kdf(Botan::KDF::create_or_throw(kdf)) { + BOTAN_ASSERT_NONNULL(m_kdf); + } + + /** + * This returns the length of the encapsulated key in bytes. For such a + * hybrid key encapsulation, this comprises the length of the KEX's public + * key (ephemeral key pair) and the length of the KEM's encapsulated key. + */ + size_t encapsulated_key_length() const override { + return m_hybrid_sk.kex_public_key().public_key_bits().size() + m_kem_decryptor.encapsulated_key_length(); + } + + /** + * This returns the length of the output shared secret in bytes. It is + * the output length of the KDF, which acts as the "combiner" of the + * shared secrets of both algorithms. + */ + size_t shared_key_length(size_t desired_shared_key_length) const override { return desired_shared_key_length; } + + /** + * This method performs the actual hybrid key decapsulation operation. + */ + void kem_decrypt(std::span out_shared_key, + std::span encapsulated_key, + size_t desired_shared_key_length, + std::span salt) override { + BOTAN_ASSERT_NOMSG(encapsulated_key.size() == encapsulated_key_length()); + + // The basic idea of the hybrid operation: + // 1. Extract the ephemeral public key and the KEM's encapsulation + // from the hybrid encapsulation, + // 2. Agree on a shared secret using the KEX's private key and the + // ephemeral public key (from the other party), + // 3. Decapsulate a shared secret using the KEM's private key and + // the KEM's encapsulation (from the other party), + // 4. Concatenate the shared secrets of both algorithms and pass the + // result through a user-defined key derivation function to form a + // "hybrid shared secret" (to be used by the application). + + // 1. Hybrid: Extract the ephemeral public key and the encapsulation. + const auto kex_encapsed_key = + encapsulated_key.subspan(0, m_hybrid_sk.kex_public_key().public_key_bits().size()); + const auto kem_encapsed_key = encapsulated_key.subspan(kex_encapsed_key.size()); + + // 2. KEX: Agree on a shared secret using the KEX's private key and the + // ephemeral public key of the other party. + const auto kex_shared_key = m_key_agreement.derive_key(0 /* no KDF */, kex_encapsed_key).bits_of(); + + // 3. KEM: Decapsulate a shared secret using the KEM's private key and + // the encapsulation of the other party. + const auto kem_shared_key = m_kem_decryptor.decrypt(kem_encapsed_key); + + // 4. Hybrid: Combine the shared secrets of both algorithms. + Botan::secure_vector concat_shared_key; + concat_shared_key.insert(concat_shared_key.end(), kex_shared_key.begin(), kex_shared_key.end()); + concat_shared_key.insert(concat_shared_key.end(), kem_shared_key.begin(), kem_shared_key.end()); + + BOTAN_ASSERT_NOMSG(out_shared_key.size() >= desired_shared_key_length); + m_kdf->derive_key(out_shared_key.first(desired_shared_key_length), concat_shared_key, salt, {}); + } + + private: + const Hybrid_PrivateKey& m_hybrid_sk; + Botan::PK_Key_Agreement m_key_agreement; + Botan::PK_KEM_Decryptor m_kem_decryptor; + std::unique_ptr m_kdf; +}; + +} // namespace + +std::unique_ptr Hybrid_PublicKey::create_kem_encryption_op(std::string_view params, + std::string_view) const { + return std::make_unique(*this, params); +} + +std::unique_ptr Hybrid_PrivateKey::create_kem_decryption_op( + Botan::RandomNumberGenerator& rng, std::string_view params, std::string_view) const { + return std::make_unique(*this, rng, params); +} + +std::unique_ptr Hybrid_PublicKey::generate_another(Botan::RandomNumberGenerator& rng) const { + return std::make_unique(m_kex_pk->generate_another(rng), m_kem_pk->generate_another(rng)); +} + +int main() { + Botan::AutoSeeded_RNG rng; + + // Alice generates two key pairs suitable for: + // 1) key exchange (X25519), and + // 2) key encapsulation (Kyber). + // + // She then combines them into a custom "hybrid" key pair that acts + // like a key encapsulation mechanism (KEM). + const auto private_key_of_alice = std::make_unique( + Botan::create_private_key("Curve25519", rng), Botan::create_private_key("Kyber", rng, "Kyber-768-r3")); + const auto public_key_of_alice = private_key_of_alice->public_key(); + + // Bob uses Alice's public key to encapsulate a shared secret, and + // derives a shared key from it using HKDF. + Botan::PK_KEM_Encryptor kem_enc(*public_key_of_alice, "HKDF(SHA-256)"); + const auto encapsulation_by_bob = kem_enc.encrypt(rng); + + // Alice decapsulates the shared secret from Bob's encapsulation using her + // private key, and derives a matching shared key using HKDF. + Botan::PK_KEM_Decryptor kem_dec(*private_key_of_alice, rng, "HKDF(SHA-256)"); + const auto shared_key = kem_dec.decrypt(encapsulation_by_bob.encapsulated_shared_key()); + + // Check that Alice and Bob now share the same secret + std::cout << "Alice's shared key: " << Botan::hex_encode(shared_key) << "\n" + << "Bob's shared key: " << Botan::hex_encode(encapsulation_by_bob.shared_key()) << "\n"; + + if(shared_key == encapsulation_by_bob.shared_key()) { + std::cout << '\n' << "Alice and Bob share the same secret!\n"; + return 0; + } else { + return 1; + } +} diff --git a/src/examples/tls_stream_client.cpp b/src/examples/tls_stream_client.cpp index f168301cc4b..11803c61d2f 100644 --- a/src/examples/tls_stream_client.cpp +++ b/src/examples/tls_stream_client.cpp @@ -7,6 +7,7 @@ #include #include #include + #include #include #include @@ -35,12 +36,14 @@ class client { public: client(boost::asio::io_context& io_context, boost::asio::ip::tcp::resolver::iterator endpoint_iterator, + std::string_view host, const http::request& req) : m_request(req), m_ctx(std::make_shared(std::make_shared(), std::make_shared(), std::make_shared(), - std::make_shared())), + std::make_shared(), + host)), m_stream(io_context, m_ctx) { boost::asio::async_connect(m_stream.lowest_layer(), std::move(endpoint_iterator), @@ -92,21 +95,33 @@ class client { Botan::TLS::Stream m_stream; }; -int main() { +int main(int argc, char* argv[]) { + if(argc != 4) { + std::cerr << "Usage: tls_stream_client \n" + << "Example:\n" + << " tls_stream_client botan.randombit.net 443 /news.html\n"; + return 1; + } + + const auto host = argv[1]; + const auto port = argv[2]; + const auto target = argv[3]; + try { boost::asio::io_context io_context; boost::asio::ip::tcp::resolver resolver(io_context); - boost::asio::ip::tcp::resolver::query query("botan.randombit.net", "443"); + boost::asio::ip::tcp::resolver::query query(host, port); boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query); http::request req; req.version(11); req.method(http::verb::get); - req.target("/news.html"); - req.set(http::field::host, "botan.randombit.net"); + req.target(target); + req.set(http::field::host, host); + req.set(http::field::user_agent, Botan::version_string()); - client c(io_context, iterator, req); + client c(io_context, iterator, host, req); io_context.run(); } catch(std::exception& e) { @@ -120,7 +135,8 @@ int main() { #else int main() { - std::cout << "Your boost version is too old, sorry.\n"; + std::cout << "Your boost version is too old, sorry.\n" + << "Or did you compile Botan without --with-boost?\n"; return 1; } diff --git a/src/examples/tls_stream_coroutine_client.cpp b/src/examples/tls_stream_coroutine_client.cpp new file mode 100644 index 00000000000..c4b18957b2e --- /dev/null +++ b/src/examples/tls_stream_coroutine_client.cpp @@ -0,0 +1,140 @@ + +#include + +#include + +// Boost 1.81.0 introduced support for the finalized C++20 coroutines +// in clang 14 and newer. Older versions of Boost might work with other +// compilers, though. +#if defined(BOTAN_FOUND_COMPATIBLE_BOOST_ASIO_VERSION) && BOOST_VERSION >= 108100 + + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include + #include + +namespace beast = boost::beast; +namespace http = beast::http; +namespace net = boost::asio; +namespace tls = Botan::TLS; +using tcp = boost::asio::ip::tcp; + +namespace { + +/** + * A simple credentials manager that uses the system's trust store + * to verify certificates. + */ +class System_Credentials_Manager : public Botan::Credentials_Manager { + public: + std::vector trusted_certificate_authorities(const std::string&, + const std::string&) override { + return {&m_cert_store}; + } + + private: + Botan::System_Certificate_Store m_cert_store; +}; + +/** + * Helper function to set up a Botan TLS Context for the ASIO wrapper. + */ +std::shared_ptr prepare_context_for(std::string_view server_info) { + auto rng = std::make_shared(); + return std::make_shared(std::make_shared(), + rng, + std::make_shared(rng), + std::make_shared(), + tls::Server_Information(server_info)); +} + +http::request create_GET_request(const std::string& host, const std::string& target) { + http::request req; + req.version(11); + req.method(http::verb::get); + req.target(target); + req.set(http::field::host, host); + req.set(http::field::user_agent, Botan::version_string()); + return req; +} + +} // namespace + +static net::awaitable request(std::string host, std::string port, std::string target) { + // Lookup host address + auto resolver = net::use_awaitable.as_default_on(tcp::resolver(co_await net::this_coro::executor)); + const auto dns_result = co_await resolver.async_resolve(host, port); + + // Connect to host and establish a TLS session + auto tls_stream = + tls::Stream(prepare_context_for(host), + net::use_awaitable.as_default_on(beast::tcp_stream(co_await net::this_coro::executor))); + tls_stream.next_layer().expires_after(std::chrono::seconds(30)); + co_await tls_stream.next_layer().async_connect(dns_result); + co_await tls_stream.async_handshake(tls::Connection_Side::Client); + + // Send HTTP GET request + tls_stream.next_layer().expires_after(std::chrono::seconds(30)); + co_await http::async_write(tls_stream, create_GET_request(host, target)); + + // Receive HTTP response and print result + beast::flat_buffer b; + http::response res; + co_await http::async_read(tls_stream, b, res); + std::cout << res << std::endl; + + // Terminate connection + co_await tls_stream.async_shutdown(); + tls_stream.next_layer().close(); +} + +int main(int argc, char* argv[]) { + if(argc != 4) { + std::cerr << "Usage: tls_stream_coroutine_client \n" + << "Example:\n" + << " tls_stream_coroutine_client botan.randombit.net 443 /news.html\n"; + return 1; + } + + const auto host = argv[1]; + const auto port = argv[2]; + const auto target = argv[3]; + + net::io_context ioc; + + int return_code = 0; + net::co_spawn(ioc, request(host, port, target), [&](std::exception_ptr eptr) { + if(eptr) { + try { + std::rethrow_exception(eptr); + } catch(std::exception& ex) { + std::cerr << "Error: " << ex.what() << "\n"; + return_code = 1; + } + } + }); + + ioc.run(); + + return return_code; +} + +#else + +int main() { + std::cout << "Your boost version is too old, sorry.\n" + << "Or did you compile Botan without --with-boost?\n"; + return 1; +} + +#endif diff --git a/src/lib/block/serpent/serpent.cpp b/src/lib/block/serpent/serpent.cpp index 8f4dc308422..d9143222efe 100644 --- a/src/lib/block/serpent/serpent.cpp +++ b/src/lib/block/serpent/serpent.cpp @@ -60,7 +60,7 @@ void Serpent::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const const Key_Inserter key_xor(m_round_key.data()); - BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i) { + for(size_t i = 0; i < blocks; ++i) { uint32_t B0, B1, B2, B3; load_le(in + 16 * i, B0, B1, B2, B3); @@ -208,7 +208,7 @@ void Serpent::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const const Key_Inserter key_xor(m_round_key.data()); - BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i) { + for(size_t i = 0; i < blocks; ++i) { uint32_t B0, B1, B2, B3; load_le(in + 16 * i, B0, B1, B2, B3); diff --git a/src/lib/block/threefish_512/threefish_512.cpp b/src/lib/block/threefish_512/threefish_512.cpp index 5b5f7ce3fc2..3a08d515520 100644 --- a/src/lib/block/threefish_512/threefish_512.cpp +++ b/src/lib/block/threefish_512/threefish_512.cpp @@ -196,7 +196,7 @@ void Threefish_512::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const Key_Inserter key(m_K.data(), m_T.data()); - BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i) { + for(size_t i = 0; i < blocks; ++i) { uint64_t X0, X1, X2, X3, X4, X5, X6, X7; load_le(in + BLOCK_SIZE * i, X0, X1, X2, X3, X4, X5, X6, X7); @@ -223,7 +223,7 @@ void Threefish_512::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const Key_Inserter key(m_K.data(), m_T.data()); - BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i) { + for(size_t i = 0; i < blocks; ++i) { uint64_t X0, X1, X2, X3, X4, X5, X6, X7; load_le(in + BLOCK_SIZE * i, X0, X1, X2, X3, X4, X5, X6, X7); diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index 02db89828fc..44f4b467819 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -538,6 +538,11 @@ BOTAN_FFI_EXPORT(2, 0) int botan_cipher_valid_nonce_length(botan_cipher_t cipher */ BOTAN_FFI_EXPORT(2, 0) int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tag_size); +/** +* Returns 1 iff the cipher provides authentication as well as confidentiality. +*/ +BOTAN_FFI_EXPORT(3, 3) int botan_cipher_is_authenticated(botan_cipher_t cipher); + /** * Get the default nonce length of this cipher */ diff --git a/src/lib/ffi/ffi_cipher.cpp b/src/lib/ffi/ffi_cipher.cpp index 5d655c18b44..3646613a79a 100644 --- a/src/lib/ffi/ffi_cipher.cpp +++ b/src/lib/ffi/ffi_cipher.cpp @@ -241,6 +241,10 @@ int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tl) { return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *tl = c.tag_size(); }); } +int botan_cipher_is_authenticated(botan_cipher_t cipher) { + return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return c.authenticated() ? 1 : 0; }); +} + int botan_cipher_name(botan_cipher_t cipher, char* name, size_t* name_len) { return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return write_str_output(name, name_len, c.name()); }); } diff --git a/src/lib/math/bigint/big_ops2.cpp b/src/lib/math/bigint/big_ops2.cpp index cc90bf45fba..9441a533126 100644 --- a/src/lib/math/bigint/big_ops2.cpp +++ b/src/lib/math/bigint/big_ops2.cpp @@ -240,7 +240,7 @@ word BigInt::operator%=(word mod) { } else { const size_t sw = sig_words(); for(size_t i = sw; i > 0; --i) { - remainder = bigint_modop(remainder, word_at(i - 1), mod); + remainder = bigint_modop_vartime(remainder, word_at(i - 1), mod); } } diff --git a/src/lib/math/bigint/big_ops3.cpp b/src/lib/math/bigint/big_ops3.cpp index f5bb2805e71..1fd5dcbae1a 100644 --- a/src/lib/math/bigint/big_ops3.cpp +++ b/src/lib/math/bigint/big_ops3.cpp @@ -158,7 +158,7 @@ word operator%(const BigInt& n, word mod) { } else { const size_t sw = n.sig_words(); for(size_t i = sw; i > 0; --i) { - remainder = bigint_modop(remainder, n.word_at(i - 1), mod); + remainder = bigint_modop_vartime(remainder, n.word_at(i - 1), mod); } } diff --git a/src/lib/math/bigint/bigint.cpp b/src/lib/math/bigint/bigint.cpp index 68d103e85d0..fef58d986a4 100644 --- a/src/lib/math/bigint/bigint.cpp +++ b/src/lib/math/bigint/bigint.cpp @@ -430,6 +430,40 @@ void BigInt::ct_cond_add(bool predicate, const BigInt& value) { bigint_cnd_add(static_cast(predicate), this->mutable_data(), this->size(), value.data(), value.sig_words()); } +void BigInt::ct_shift_left(size_t shift) { + auto shl_bit = [](const BigInt& a, BigInt& result) { + BOTAN_DEBUG_ASSERT(a.size() + 1 == result.size()); + bigint_shl2(result.mutable_data(), a.data(), a.size(), 0, 1); + // shl2 may have shifted a bit into the next word, which must be dropped + clear_mem(result.mutable_data() + result.size() - 1, 1); + }; + + auto shl_word = [](const BigInt& a, BigInt& result) { + // the most significant word is not copied, aka. shifted out + bigint_shl2(result.mutable_data(), a.data(), a.size() - 1 /* ignore msw */, 1, 0); + // we left-shifted by a full word, the least significant word must be zero'ed + clear_mem(result.mutable_data(), 1); + }; + + BOTAN_ASSERT_NOMSG(size() > 0); + + constexpr size_t bits_in_word = sizeof(word) * 8; + const size_t word_shift = shift >> ceil_log2(bits_in_word); // shift / bits_in_word + const size_t bit_shift = shift & ((1 << ceil_log2(bits_in_word)) - 1); // shift % bits_in_word + const size_t iterations = std::max(size(), bits_in_word) - 1; // uint64_t i; i << 64 is undefined behaviour + + // In every iteration, shift one bit and one word to the left and use the + // shift results only when they are within the shift range. + BigInt tmp; + tmp.resize(size() + 1 /* to hold the shifted-out word */); + for(size_t i = 0; i < iterations; ++i) { + shl_bit(*this, tmp); + ct_cond_assign(i < bit_shift, tmp); + shl_word(*this, tmp); + ct_cond_assign(i < word_shift, tmp); + } +} + void BigInt::ct_cond_swap(bool predicate, BigInt& other) { const size_t max_words = std::max(size(), other.size()); grow_to(max_words); diff --git a/src/lib/math/bigint/bigint.h b/src/lib/math/bigint/bigint.h index 5a7e65a4e85..7c16b5045ed 100644 --- a/src/lib/math/bigint/bigint.h +++ b/src/lib/math/bigint/bigint.h @@ -658,6 +658,10 @@ class BOTAN_PUBLIC_API(2, 0) BigInt final { * If len exactly equals this->bytes(), this function behaves identically * to binary_encode. * + * Zero-padding the binary encoding is useful to ensure that other + * applications correctly parse the encoded value as "positive integer", + * as a leading 1-bit may be interpreted as a sign bit. + * * @param buf destination byte array for the integer value * @param len how many bytes to write */ @@ -702,6 +706,12 @@ class BOTAN_PUBLIC_API(2, 0) BigInt final { */ void ct_cond_add(bool predicate, const BigInt& value); + /** + * Shift @p shift bits to the left, runtime is independent of + * the value of @p shift. + */ + void ct_shift_left(size_t shift); + /** * If predicate is true flip the sign of *this */ diff --git a/src/lib/math/bigint/divide.cpp b/src/lib/math/bigint/divide.cpp index 58d8fd3b2b9..341ebd8f0b9 100644 --- a/src/lib/math/bigint/divide.cpp +++ b/src/lib/math/bigint/divide.cpp @@ -200,7 +200,7 @@ void vartime_divide(const BigInt& x, const BigInt& y_arg, BigInt& q_out, BigInt& const word x_j1 = r.word_at(j - 1); const word x_j2 = r.word_at(j - 2); - word qjt = bigint_divop(x_j0, x_j1, y_t0); + word qjt = bigint_divop_vartime(x_j0, x_j1, y_t0); qjt = CT::Mask::is_equal(x_j0, y_t0).select(MP_WORD_MAX, qjt); diff --git a/src/lib/math/mp/mp_core.h b/src/lib/math/mp/mp_core.h index bd70e5cc626..a6127f8174e 100644 --- a/src/lib/math/mp/mp_core.h +++ b/src/lib/math/mp/mp_core.h @@ -662,9 +662,9 @@ inline void bigint_mod_sub_n(word t[], const word s[], const word mod[], word ws /** * Compute ((n1<(n1) << BOTAN_MP_WORD_BITS) | n0) % d; #else - word z = bigint_divop(n1, n0, d); + word z = bigint_divop_vartime(n1, n0, d); word dummy = 0; z = word_madd2(z, d, &dummy); return (n0 - z); diff --git a/src/lib/math/numbertheory/numthry.cpp b/src/lib/math/numbertheory/numthry.cpp index ce05296b0d5..c1b592c7b23 100644 --- a/src/lib/math/numbertheory/numthry.cpp +++ b/src/lib/math/numbertheory/numthry.cpp @@ -20,18 +20,6 @@ namespace Botan { -namespace { - -void sub_abs(BigInt& z, const BigInt& x, const BigInt& y) { - const size_t x_sw = x.sig_words(); - const size_t y_sw = y.sig_words(); - z.resize(std::max(x_sw, y_sw)); - - bigint_sub_abs(z.mutable_data(), x.data(), x_sw, y.data(), y_sw); -} - -} // namespace - /* * Tonelli-Shanks algorithm */ @@ -199,17 +187,8 @@ size_t low_zero_bits(const BigInt& n) { return static_cast(seen_nonempty_word.if_set_return(low_zero)); } -namespace { - -size_t safegcd_loop_bound(size_t f_bits, size_t g_bits) { - const size_t d = std::max(f_bits, g_bits); - return 4 + 3 * d; -} - -} // namespace - /* -* Calculate the GCD +* Calculate the GCD in constant time */ BigInt gcd(const BigInt& a, const BigInt& b) { if(a.is_zero()) { @@ -218,57 +197,73 @@ BigInt gcd(const BigInt& a, const BigInt& b) { if(b.is_zero()) { return abs(a); } - if(a == 1 || b == 1) { - return BigInt::one(); - } - - // See https://gcd.cr.yp.to/safegcd-20190413.pdf fig 1.2 - BigInt f = a; - BigInt g = b; - f.const_time_poison(); - g.const_time_poison(); + const size_t sz = std::max(a.sig_words(), b.sig_words()); + auto u = BigInt::with_capacity(sz); + auto v = BigInt::with_capacity(sz); + u += a; + v += b; - f.set_sign(BigInt::Positive); - g.set_sign(BigInt::Positive); + u.const_time_poison(); + v.const_time_poison(); - const size_t common2s = std::min(low_zero_bits(f), low_zero_bits(g)); - CT::unpoison(common2s); + u.set_sign(BigInt::Positive); + v.set_sign(BigInt::Positive); - f >>= common2s; - g >>= common2s; + // In the worst case we have two fully populated big ints. After right + // shifting so many times, we'll have reached the result for sure. + const size_t loop_cnt = u.bits() + v.bits(); - f.ct_cond_swap(f.is_even(), g); + using WordMask = CT::Mask; - int32_t delta = 1; - - const size_t loop_cnt = safegcd_loop_bound(f.bits(), g.bits()); - - BigInt newg, t; + // This temporary is big enough to hold all intermediate results of the + // algorithm. No reallocation will happen during the loop. + // Note however, that `ct_cond_assign()` will invalidate the 'sig_words' + // cache, which _does not_ shrink the capacity of the underlying buffer. + auto tmp = BigInt::with_capacity(sz); + size_t factors_of_two = 0; for(size_t i = 0; i != loop_cnt; ++i) { - sub_abs(newg, f, g); + auto both_odd = WordMask::expand(u.is_odd()) & WordMask::expand(v.is_odd()); - const bool need_swap = (g.is_odd() && delta > 0); + // Subtract the smaller from the larger if both are odd + auto u_gt_v = WordMask::expand(bigint_cmp(u.data(), u.size(), v.data(), v.size()) > 0); + bigint_sub_abs(tmp.mutable_data(), u.data(), sz, v.data(), sz); + u.ct_cond_assign((u_gt_v & both_odd).as_bool(), tmp); + v.ct_cond_assign((~u_gt_v & both_odd).as_bool(), tmp); - // if(need_swap) { delta *= -1 } else { delta *= 1 } - delta *= CT::Mask::expand(need_swap).if_not_set_return(2) - 1; - f.ct_cond_swap(need_swap, g); - g.ct_cond_swap(need_swap, newg); + const auto u_is_even = WordMask::expand(u.is_even()); + const auto v_is_even = WordMask::expand(v.is_even()); + BOTAN_DEBUG_ASSERT((u_is_even | v_is_even).as_bool()); - delta += 1; + // When both are even, we're going to eliminate a factor of 2. + // We have to reapply this factor to the final result. + factors_of_two += (u_is_even & v_is_even).if_set_return(1); - g.ct_cond_add(g.is_odd(), f); - g >>= 1; + // remove one factor of 2, if u is even + bigint_shr2(tmp.mutable_data(), u.data(), sz, 0, 1); + u.ct_cond_assign(u_is_even.as_bool(), tmp); + + // remove one factor of 2, if v is even + bigint_shr2(tmp.mutable_data(), v.data(), sz, 0, 1); + v.ct_cond_assign(v_is_even.as_bool(), tmp); } - f <<= common2s; + // The GCD (without factors of two) is either in u or v, the other one is + // zero. The non-zero variable _must_ be odd, because all factors of two were + // removed in the loop iterations above. + BOTAN_DEBUG_ASSERT(u.is_zero() || v.is_zero()); + BOTAN_DEBUG_ASSERT(u.is_odd() || v.is_odd()); + + // make sure that the GCD (without factors of two) is in u + u.ct_cond_assign(u.is_even() /* .is_zero() would not be constant time */, v); - f.const_time_unpoison(); - g.const_time_unpoison(); + // re-apply the factors of two + u.ct_shift_left(factors_of_two); - BOTAN_ASSERT_NOMSG(g.is_zero()); + u.const_time_unpoison(); + v.const_time_unpoison(); - return f; + return u; } /* diff --git a/src/lib/modes/cipher_mode.cpp b/src/lib/modes/cipher_mode.cpp index 942f961dd9b..0b966165311 100644 --- a/src/lib/modes/cipher_mode.cpp +++ b/src/lib/modes/cipher_mode.cpp @@ -53,13 +53,11 @@ std::unique_ptr Cipher_Mode::create(std::string_view algo, std::string_view provider) { #if defined(BOTAN_HAS_COMMONCRYPTO) if(provider.empty() || provider == "commoncrypto") { - auto commoncrypto_cipher = make_commoncrypto_cipher_mode(algo, direction); - - if(commoncrypto_cipher) - return commoncrypto_cipher; + if(auto cm = make_commoncrypto_cipher_mode(algo, direction)) + return cm; if(!provider.empty()) - return std::unique_ptr(); + return nullptr; } #endif diff --git a/src/lib/permutations/keccak_perm/keccak_helpers.h b/src/lib/permutations/keccak_perm/keccak_helpers.h index 6810eab9c57..9d322262e78 100644 --- a/src/lib/permutations/keccak_perm/keccak_helpers.h +++ b/src/lib/permutations/keccak_perm/keccak_helpers.h @@ -9,13 +9,13 @@ #ifndef BOTAN_KECCAK_HELPERS_H_ #define BOTAN_KECCAK_HELPERS_H_ +#include + #include +#include #include #include -#include -#include - namespace Botan { /** @@ -87,7 +87,7 @@ concept absorbing_object = updatable_object || appendable_object; * @returns the number of bytes absorbed into the @p xof */ template - requires(concepts::constructible_from, Ts> && ...) + requires(std::constructible_from, Ts> && ...) size_t keccak_absorb_padded_strings_encoding(T& sink, size_t padding_mod, Ts... byte_strings) { BOTAN_ASSERT_NOMSG(padding_mod > 0); diff --git a/src/lib/prov/pkcs11/p11_ecdh.cpp b/src/lib/prov/pkcs11/p11_ecdh.cpp index f7881d6400d..a9849c2a03c 100644 --- a/src/lib/prov/pkcs11/p11_ecdh.cpp +++ b/src/lib/prov/pkcs11/p11_ecdh.cpp @@ -11,9 +11,9 @@ #if defined(BOTAN_HAS_ECDH) #include + #include #include #include - #include namespace Botan::PKCS11 { diff --git a/src/lib/prov/pkcs11/p11_ecdsa.cpp b/src/lib/prov/pkcs11/p11_ecdsa.cpp index 3fc4589c087..e374463b644 100644 --- a/src/lib/prov/pkcs11/p11_ecdsa.cpp +++ b/src/lib/prov/pkcs11/p11_ecdsa.cpp @@ -10,10 +10,10 @@ #if defined(BOTAN_HAS_ECDSA) + #include #include #include #include - #include namespace Botan::PKCS11 { diff --git a/src/lib/prov/pkcs11/p11_rsa.cpp b/src/lib/prov/pkcs11/p11_rsa.cpp index c891574ee0e..e54680d38cd 100644 --- a/src/lib/prov/pkcs11/p11_rsa.cpp +++ b/src/lib/prov/pkcs11/p11_rsa.cpp @@ -16,7 +16,6 @@ #include #include #include - #include #include namespace Botan::PKCS11 { diff --git a/src/lib/prov/tpm/tpm.cpp b/src/lib/prov/tpm/tpm.cpp index b8d9d537eab..3eeff4311b7 100644 --- a/src/lib/prov/tpm/tpm.cpp +++ b/src/lib/prov/tpm/tpm.cpp @@ -9,10 +9,10 @@ #include #include +#include #include #include #include -#include #include #include diff --git a/src/lib/pubkey/ec_group/ec_group.cpp b/src/lib/pubkey/ec_group/ec_group.cpp index 068d75bbd46..5f3945fcbe5 100644 --- a/src/lib/pubkey/ec_group/ec_group.cpp +++ b/src/lib/pubkey/ec_group/ec_group.cpp @@ -357,8 +357,12 @@ std::pair, bool> EC_Group::BER_decode_EC_group(co .end_cons() .verify_end(); - if(p.bits() < 64 || p.is_negative() || !is_bailie_psw_probable_prime(p)) { - throw Decoding_Error("Invalid ECC p parameter"); + if(p.bits() < 112 || p.bits() > 1024) { + throw Decoding_Error("ECC p parameter is invalid size"); + } + + if(p.is_negative() || !is_bailie_psw_probable_prime(p)) { + throw Decoding_Error("ECC p parameter is not a prime"); } if(a.is_negative() || a >= p) { diff --git a/src/lib/pubkey/info.txt b/src/lib/pubkey/info.txt index f91d90faf11..bad0debd1d2 100644 --- a/src/lib/pubkey/info.txt +++ b/src/lib/pubkey/info.txt @@ -11,6 +11,7 @@ brief -> "Implementations of public key schemes" pk_algs.h pk_keys.h pk_ops_fwd.h +pk_ops.h pkcs8.h pubkey.h x509_key.h @@ -18,7 +19,6 @@ x509_key.h blinding.h -pk_ops.h pk_ops_impl.h workfactor.h diff --git a/src/lib/pubkey/kyber/kyber_common/kyber.cpp b/src/lib/pubkey/kyber/kyber_common/kyber.cpp index fe9796325a5..66839eb700b 100644 --- a/src/lib/pubkey/kyber/kyber_common/kyber.cpp +++ b/src/lib/pubkey/kyber/kyber_common/kyber.cpp @@ -71,6 +71,20 @@ KyberMode::Mode kyber_mode_from_string(std::string_view str) { throw Invalid_Argument(fmt("'{}' is not a valid Kyber mode name", str)); } +/** + * Constant time implementation for computing an unsigned integer division + * with KyberConstants::Q = 3329. + * + * It enforces the optimization of various compilers, + * replacing the division operation with multiplication and shifts. + * + * @returns (a / KyberConstants::Q) + */ +uint16_t ct_int_div_kyber_q(uint32_t a) { + const uint64_t tmp = (static_cast(a) * 989558401UL) >> 32; + return static_cast((tmp + ((a - tmp) >> 1)) >> 11); +} + } // namespace KyberMode::KyberMode(Mode mode) : m_mode(mode) {} @@ -399,21 +413,12 @@ class Polynomial { this->csubq(); - auto compress = [](uint32_t t) { - // (t << 1) + ((KyberConstants::Q / 2) / KyberConstants::Q) & 1 - // Note that magic numbers assume that ::Q = 3329 - t <<= 1; - t += 1665; - t *= 80635; - t >>= 28; - t &= 1; - return static_cast(t); - }; - for(size_t i = 0; i < size() / 8; ++i) { result[i] = 0; for(size_t j = 0; j < 8; ++j) { - result[i] |= compress(this->m_coeffs[8 * i + j]) << j; + const uint16_t t = + ct_int_div_kyber_q((static_cast(this->m_coeffs[8 * i + j]) << 1) + KyberConstants::Q / 2); + result[i] |= (t & 1) << j; } } @@ -564,7 +569,7 @@ class Polynomial { */ static int16_t barrett_reduce(int16_t a) { int16_t t; - const int16_t v = ((1U << 26) + KyberConstants::Q / 2) / KyberConstants::Q; + constexpr int16_t v = ((1U << 26) + KyberConstants::Q / 2) / KyberConstants::Q; t = static_cast(v) * a >> 26; t *= KyberConstants::Q; @@ -865,8 +870,7 @@ class Ciphertext { size_t offset = 0; for(size_t i = 0; i < p.size() / 8; ++i) { for(size_t j = 0; j < 8; ++j) { - t[j] = - (((static_cast(p[8 * i + j]) << 4) + KyberConstants::Q / 2) / KyberConstants::Q) & 15; + t[j] = ct_int_div_kyber_q((static_cast(p[8 * i + j]) << 4) + KyberConstants::Q / 2) & 15; } r[0 + offset] = t[0] | (t[1] << 4); @@ -879,8 +883,7 @@ class Ciphertext { size_t offset = 0; for(size_t i = 0; i < p.size() / 8; ++i) { for(size_t j = 0; j < 8; ++j) { - t[j] = - (((static_cast(p[8 * i + j]) << 5) + KyberConstants::Q / 2) / KyberConstants::Q) & 31; + t[j] = ct_int_div_kyber_q((static_cast(p[8 * i + j]) << 5) + KyberConstants::Q / 2) & 31; } r[0 + offset] = (t[0] >> 0) | (t[1] << 5); diff --git a/src/lib/pubkey/mce/mce_internal.h b/src/lib/pubkey/mce/mce_internal.h index e34de9e438a..4e14ff75dcd 100644 --- a/src/lib/pubkey/mce/mce_internal.h +++ b/src/lib/pubkey/mce/mce_internal.h @@ -13,7 +13,7 @@ #define BOTAN_MCELIECE_INTERNAL_H_ #include -#include +#include #include namespace Botan { diff --git a/src/lib/pubkey/pk_keys.cpp b/src/lib/pubkey/pk_keys.cpp index 300fe05b2e3..336be14255c 100644 --- a/src/lib/pubkey/pk_keys.cpp +++ b/src/lib/pubkey/pk_keys.cpp @@ -10,8 +10,8 @@ #include #include #include +#include #include -#include namespace Botan { diff --git a/src/lib/pubkey/pk_ops.h b/src/lib/pubkey/pk_ops.h index 846ceb94bc7..d6a9f046d5e 100644 --- a/src/lib/pubkey/pk_ops.h +++ b/src/lib/pubkey/pk_ops.h @@ -16,6 +16,11 @@ * Unless you're doing something like that, you don't need anything * here. Instead use pubkey.h which wraps these types safely and * provides a stable application-oriented API. +* +* Note: This header was accidentally pulled from the public API between +* Botan 3.0.0 and 3.2.0, and then restored in 3.3.0. If you are +* maintaining an application which used this header in Botan 2.x, +* you should make sure to use Botan 3.3.0 or later when migrating. */ #include @@ -33,7 +38,7 @@ namespace PK_Ops { /** * Public key encryption interface */ -class Encryption { +class BOTAN_UNSTABLE_API Encryption { public: virtual secure_vector encrypt(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) = 0; @@ -47,7 +52,7 @@ class Encryption { /** * Public key decryption interface */ -class Decryption { +class BOTAN_UNSTABLE_API Decryption { public: virtual secure_vector decrypt(uint8_t& valid_mask, const uint8_t ciphertext[], @@ -61,7 +66,7 @@ class Decryption { /** * Public key signature verification interface */ -class Verification { +class BOTAN_UNSTABLE_API Verification { public: /** * Add more data to the message currently being signed @@ -86,7 +91,7 @@ class Verification { /** * Public key signature creation interface */ -class Signature { +class BOTAN_UNSTABLE_API Signature { public: /** * Add more data to the message currently being signed @@ -124,7 +129,7 @@ class Signature { /** * A generic key agreement operation (eg DH or ECDH) */ -class Key_Agreement { +class BOTAN_UNSTABLE_API Key_Agreement { public: virtual secure_vector agree( size_t key_len, const uint8_t other_key[], size_t other_key_len, const uint8_t salt[], size_t salt_len) = 0; @@ -137,7 +142,7 @@ class Key_Agreement { /** * KEM (key encapsulation) */ -class KEM_Encryption { +class BOTAN_UNSTABLE_API KEM_Encryption { public: virtual void kem_encrypt(std::span out_encapsulated_key, std::span out_shared_key, @@ -152,7 +157,7 @@ class KEM_Encryption { virtual ~KEM_Encryption() = default; }; -class KEM_Decryption { +class BOTAN_UNSTABLE_API KEM_Decryption { public: virtual void kem_decrypt(std::span out_shared_key, std::span encapsulated_key, diff --git a/src/lib/pubkey/pk_ops_impl.h b/src/lib/pubkey/pk_ops_impl.h index d95f960158b..990ec3e6a7f 100644 --- a/src/lib/pubkey/pk_ops_impl.h +++ b/src/lib/pubkey/pk_ops_impl.h @@ -10,8 +10,8 @@ #include #include +#include #include -#include namespace Botan::PK_Ops { diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp index 35c970b0e53..903195b9003 100644 --- a/src/lib/pubkey/pubkey.cpp +++ b/src/lib/pubkey/pubkey.cpp @@ -10,11 +10,11 @@ #include #include #include +#include #include #include #include #include -#include #include namespace Botan { diff --git a/src/lib/pubkey/sm2/sm2_enc.cpp b/src/lib/pubkey/sm2/sm2_enc.cpp index 7985b2b7d0c..cb852e0d65d 100644 --- a/src/lib/pubkey/sm2/sm2_enc.cpp +++ b/src/lib/pubkey/sm2/sm2_enc.cpp @@ -11,9 +11,9 @@ #include #include #include +#include #include #include -#include #include namespace Botan { diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp index c7e3fad5878..92a54840aac 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp @@ -6,9 +6,9 @@ * Botan is released under the Simplified BSD License (see license.txt) **/ -#include #include +#include #include #include #include diff --git a/src/lib/pubkey/xmss/xmss_signature_operation.h b/src/lib/pubkey/xmss/xmss_signature_operation.h index f500a0a1580..a3fa001902b 100644 --- a/src/lib/pubkey/xmss/xmss_signature_operation.h +++ b/src/lib/pubkey/xmss/xmss_signature_operation.h @@ -8,8 +8,8 @@ #ifndef BOTAN_XMSS_SIGNATURE_OPERATION_H_ #define BOTAN_XMSS_SIGNATURE_OPERATION_H_ +#include #include -#include #include #include #include diff --git a/src/lib/pubkey/xmss/xmss_verification_operation.h b/src/lib/pubkey/xmss/xmss_verification_operation.h index 4ff02510c97..db5e7d266dc 100644 --- a/src/lib/pubkey/xmss/xmss_verification_operation.h +++ b/src/lib/pubkey/xmss/xmss_verification_operation.h @@ -8,8 +8,8 @@ #ifndef BOTAN_XMSS_VERIFICATION_OPERATION_H_ #define BOTAN_XMSS_VERIFICATION_OPERATION_H_ +#include #include -#include #include namespace Botan { diff --git a/src/lib/rng/rng.h b/src/lib/rng/rng.h index 4ef041bc783..b96e2f4633e 100644 --- a/src/lib/rng/rng.h +++ b/src/lib/rng/rng.h @@ -201,7 +201,7 @@ class BOTAN_PUBLIC_API(2, 0) RandomNumberGenerator { * @throws Exception if RNG or memory allocation fails */ template > - requires concepts::default_initializable + requires std::default_initializable T random_vec(size_t bytes) { T result; random_vec(result, bytes); diff --git a/src/lib/tls/asio/asio_stream.h b/src/lib/tls/asio/asio_stream.h index 585f40f71bb..b90317a708f 100644 --- a/src/lib/tls/asio/asio_stream.h +++ b/src/lib/tls/asio/asio_stream.h @@ -943,6 +943,15 @@ class Stream { const boost::asio::mutable_buffer m_input_buffer; }; +// deduction guides for convenient construction from an existing +// underlying transport stream T +template +Stream(std::shared_ptr, std::shared_ptr, T) -> Stream; +template +Stream(std::shared_ptr, T) -> Stream; +template +Stream(T, std::shared_ptr) -> Stream; + } // namespace Botan::TLS #endif diff --git a/src/lib/utils/compiler.h b/src/lib/utils/compiler.h index 742d1990386..94ca58b1842 100644 --- a/src/lib/utils/compiler.h +++ b/src/lib/utils/compiler.h @@ -10,6 +10,17 @@ #include +/* +NOTE: Avoid using BOTAN_COMPILER_IS_XXX macros anywhere in this file + +This macro is set based on what compiler was used to build the +library, but it is possible that the library is built with one +compiler and then the application is built using another. + +For example using BOTAN_COMPILER_IS_CLANG would trigger (incorrectly) +when the application is later compiled using GCC. +*/ + /** * Used to annotate API exports which are public and supported. * These APIs will not be broken/removed unless strictly required for @@ -156,32 +167,19 @@ #endif -/* -* Define BOTAN_PARALLEL_SIMD_FOR -*/ -#if !defined(BOTAN_PARALLEL_SIMD_FOR) - - #if defined(BOTAN_BUILD_COMPILER_IS_GCC) - #define BOTAN_PARALLEL_SIMD_FOR _Pragma("GCC ivdep") for - #else - #define BOTAN_PARALLEL_SIMD_FOR for - #endif - -#endif - -#if defined(BOTAN_BUILD_COMPILER_IS_GCC) - #define BOTAN_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") - #define BOTAN_DIAGNOSTIC_IGNORE_DEPRECATED_DECLARATIONS \ - _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") - #define BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE - #define BOTAN_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") -#elif defined(BOTAN_BUILD_COMPILER_IS_CLANG) +#if defined(__clang__) #define BOTAN_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") #define BOTAN_DIAGNOSTIC_IGNORE_DEPRECATED_DECLARATIONS \ _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") #define BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE #define BOTAN_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") -#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) +#elif defined(__GNUG__) + #define BOTAN_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define BOTAN_DIAGNOSTIC_IGNORE_DEPRECATED_DECLARATIONS \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + #define BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE + #define BOTAN_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) #define BOTAN_DIAGNOSTIC_PUSH __pragma(warning(push)) #define BOTAN_DIAGNOSTIC_IGNORE_DEPRECATED_DECLARATIONS __pragma(warning(disable : 4996)) #define BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE __pragma(warning(disable : 4250)) diff --git a/src/lib/utils/concepts.h b/src/lib/utils/concepts.h index 59a0c983346..171d9d04c3a 100644 --- a/src/lib/utils/concepts.h +++ b/src/lib/utils/concepts.h @@ -121,41 +121,6 @@ inline constexpr void assert_equal_byte_lengths(R0&& r0, Rs&&... rs) namespace concepts { -// TODO: C++20 use std::convertible_to<> that was not available in Android NDK -// as of this writing. Tested with API Level up to 33. -template -concept convertible_to = std::is_convertible_v && requires { static_cast(std::declval()); }; - -// TODO: C++20 provides concepts like std::equality_comparable or -// std::three_way_comparable, but at the time of this writing, some -// target platforms did not ship with those (Xcode 14, Android NDK r25, -// emscripten) - -template -concept equality_comparable = requires(const std::remove_reference_t& a, const std::remove_reference_t b) { - { a == b } -> convertible_to; - }; - -template -concept three_way_comparison_result = - convertible_to || convertible_to || - convertible_to; - -template -concept three_way_comparable = requires(const std::remove_reference_t& a, const std::remove_reference_t b) { - { a <=> b } -> three_way_comparison_result; - }; - -template -concept destructible = std::is_nothrow_destructible_v; - -template -concept constructible_from = destructible && std::is_constructible_v; - -template -concept default_initializable = - constructible_from && requires { T{}; } && requires { ::new(static_cast(nullptr)) T; }; - // TODO: C++20 provides concepts like std::ranges::range or ::sized_range // but at the time of this writing clang had not caught up on all // platforms. E.g. clang 14 on Xcode does not support ranges properly. @@ -222,16 +187,6 @@ concept strong_type = is_strong_type_v; template concept contiguous_strong_type = strong_type && contiguous_container; -// std::integral is a concept that is shipped with C++20 but Android NDK is not -// yet there. -// TODO: C++20 - replace with std::integral -template -concept integral = std::is_integral_v; - -// TODO: C++20 - replace with std::unsigned_integral -template -concept unsigned_integral = std::is_integral_v && std::is_unsigned_v; - } // namespace concepts } // namespace Botan diff --git a/src/lib/utils/loadstor.h b/src/lib/utils/loadstor.h index 15bc7ff7d1b..fd5d2875560 100644 --- a/src/lib/utils/loadstor.h +++ b/src/lib/utils/loadstor.h @@ -9,10 +9,10 @@ #ifndef BOTAN_LOAD_STORE_H_ #define BOTAN_LOAD_STORE_H_ -#include #include #include #include +#include #include namespace Botan { @@ -88,7 +88,7 @@ inline constexpr uint64_t make_uint64( * @param in_range a fixed-length span with some bytes * @return T loaded from in, as a big-endian value */ -template InR> +template InR> inline constexpr T load_be(InR&& in_range) { ranges::assert_exact_byte_length(in_range); std::span in{in_range}; @@ -113,7 +113,7 @@ inline constexpr T load_be(InR&& in_range) { * @param in_range a fixed-length span with some bytes * @return T loaded from in, as a little-endian value */ -template InR> +template InR> inline constexpr T load_le(InR&& in_range) { ranges::assert_exact_byte_length(in_range); std::span in{in_range}; @@ -222,7 +222,7 @@ inline constexpr T load_le(const uint8_t in[], size_t off) { * @param off an offset into the array * @return off'th unsigned integer of in, as a big-endian value */ -template +template inline constexpr T load_be(const uint8_t in[], size_t off) { // asserts that *in points to the correct amount of memory return load_be(std::span(in + off * sizeof(T), sizeof(T))); @@ -234,7 +234,7 @@ inline constexpr T load_be(const uint8_t in[], size_t off) { * @param off an offset into the array * @return off'th unsigned integer of in, as a little-endian value */ -template +template inline constexpr T load_le(const uint8_t in[], size_t off) { // asserts that *in points to the correct amount of memory return load_le(std::span(in + off * sizeof(T), sizeof(T))); @@ -245,7 +245,7 @@ inline constexpr T load_le(const uint8_t in[], size_t off) { * @param in a fixed-length span to some bytes * @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ -template InR, concepts::unsigned_integral... Ts> +template InR, std::unsigned_integral... Ts> requires(sizeof...(Ts) > 0) && all_same_v inline constexpr void load_be(InR&& in, Ts&... outs) { ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(in); @@ -262,7 +262,7 @@ inline constexpr void load_be(InR&& in, Ts&... outs) { * @param in a fixed-length span to some bytes * @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ -template InR, concepts::unsigned_integral... Ts> +template InR, std::unsigned_integral... Ts> requires(sizeof...(Ts) > 0) && all_same_v inline constexpr void load_le(InR&& in, Ts&... outs) { ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(in); @@ -279,7 +279,7 @@ inline constexpr void load_le(InR&& in, Ts&... outs) { * @param in a pointer to some bytes * @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ -template +template requires(sizeof...(Ts) > 0) && all_same_v inline constexpr void load_be(const uint8_t in[], Ts&... outs) { constexpr auto bytes = (sizeof(outs) + ...); @@ -292,7 +292,7 @@ inline constexpr void load_be(const uint8_t in[], Ts&... outs) { * @param in a pointer to some bytes * @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ -template +template requires(sizeof...(Ts) > 0) && all_same_v inline constexpr void load_le(const uint8_t in[], Ts&... outs) { constexpr auto bytes = (sizeof(outs) + ...); @@ -354,7 +354,7 @@ inline constexpr void load_be(T out[], const uint8_t in[], size_t count) { * @param in the input unsigned integer * @param out_range the fixed-length span to write to */ -template OutR> +template OutR> inline constexpr void store_be(T in, OutR&& out_range) { ranges::assert_exact_byte_length(out_range); std::span out{out_range}; @@ -379,7 +379,7 @@ inline constexpr void store_be(T in, OutR&& out_range) { * @param in the input unsigned integer * @param out_range the fixed-length span to write to */ -template OutR> +template OutR> inline constexpr void store_le(T in, OutR&& out_range) { ranges::assert_exact_byte_length(out_range); std::span out{out_range}; @@ -404,7 +404,7 @@ inline constexpr void store_le(T in, OutR&& out_range) { * @param in the input unsigned integer * @param out the byte array to write to */ -template +template inline constexpr void store_be(T in, uint8_t out[sizeof(T)]) { store_be(in, std::span(out, sizeof(T))); } @@ -414,7 +414,7 @@ inline constexpr void store_be(T in, uint8_t out[sizeof(T)]) { * @param in the input unsigned integer * @param out the byte array to write to */ -template +template inline constexpr void store_le(T in, uint8_t out[sizeof(T)]) { store_le(in, std::span(out, sizeof(T))); } @@ -424,7 +424,7 @@ inline constexpr void store_le(T in, uint8_t out[sizeof(T)]) { * @param out a fixed-length span to some bytes * @param ins a arbitrary-length parameter list of unsigned integers to be stored */ -template OutR, concepts::unsigned_integral... Ts> +template OutR, std::unsigned_integral... Ts> requires(sizeof...(Ts) > 0) && all_same_v inline constexpr void store_be(OutR&& out, Ts... ins) { ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(out); @@ -441,7 +441,7 @@ inline constexpr void store_be(OutR&& out, Ts... ins) { * @param out a fixed-length span to some bytes * @param ins a arbitrary-length parameter list of unsigned integers to be stored */ -template OutR, concepts::unsigned_integral... Ts> +template OutR, std::unsigned_integral... Ts> requires(sizeof...(Ts) > 0) && all_same_v inline constexpr void store_le(OutR&& out, Ts... ins) { ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(out); @@ -458,7 +458,7 @@ inline constexpr void store_le(OutR&& out, Ts... ins) { * @param ins a pointer to some bytes to be written * @param out a arbitrary-length parameter list of unsigned integers to be stored */ -template +template requires(sizeof...(Ts) > 0) && all_same_v inline constexpr void store_be(uint8_t out[], Ts... ins) { constexpr auto bytes = (sizeof(ins) + ...); @@ -471,7 +471,7 @@ inline constexpr void store_be(uint8_t out[], Ts... ins) { * @param ins a pointer to some bytes to be written * @param out a arbitrary-length parameter list of unsigned integers to be stored */ -template +template requires(sizeof...(Ts) > 0) && all_same_v inline constexpr void store_le(uint8_t out[], Ts... ins) { constexpr auto bytes = (sizeof(ins) + ...); @@ -484,7 +484,7 @@ inline constexpr void store_le(uint8_t out[], Ts... ins) { * @param in the input unsigned integer * @return a byte array holding the integer value in big-endian byte order */ -template +template inline constexpr auto store_be(T in) { std::array out; store_be(in, out); @@ -496,7 +496,7 @@ inline constexpr auto store_be(T in) { * @param in the input unsigned integer * @return a byte array holding the integer value in little-endian byte order */ -template +template inline constexpr auto store_le(T in) { std::array out; store_le(in, out); diff --git a/src/lib/utils/strong_type.h b/src/lib/utils/strong_type.h index e311d141354..1c05d8259f7 100644 --- a/src/lib/utils/strong_type.h +++ b/src/lib/utils/strong_type.h @@ -58,7 +58,7 @@ class Strong_Adapter : public Strong_Base { using Strong_Base::Strong_Base; }; -template +template class Strong_Adapter : public Strong_Base { public: using Strong_Base::Strong_Base; @@ -198,328 +198,328 @@ operator<<(std::ostream& os, const Strong& v) { } template - requires(concepts::equality_comparable) bool + requires(std::equality_comparable) bool operator==(const Strong& lhs, const Strong& rhs) { return lhs.get() == rhs.get(); } template - requires(concepts::three_way_comparable) + requires(std::three_way_comparable) auto operator<=>(const Strong& lhs, const Strong& rhs) { return lhs.get() <=> rhs.get(); } -template +template auto operator<=>(T1 a, Strong b) { return a <=> b.get(); } -template +template auto operator<=>(Strong a, T2 b) { return a.get() <=> b; } -template +template auto operator==(T1 a, Strong b) { return a == b.get(); } -template +template auto operator==(Strong a, T2 b) { return a.get() == b; } -template +template requires(detail::has_capability) constexpr decltype(auto) operator+(T1 a, Strong b) { return Strong(a + b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator+(Strong a, T2 b) { return Strong(a.get() + b); } -template +template constexpr decltype(auto) operator+(Strong a, Strong b) { return Strong(a.get() + b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator-(T1 a, Strong b) { return Strong(a - b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator-(Strong a, T2 b) { return Strong(a.get() - b); } -template +template constexpr decltype(auto) operator-(Strong a, Strong b) { return Strong(a.get() - b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator*(T1 a, Strong b) { return Strong(a * b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator*(Strong a, T2 b) { return Strong(a.get() * b); } -template +template constexpr decltype(auto) operator*(Strong a, Strong b) { return Strong(a.get() * b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator/(T1 a, Strong b) { return Strong(a / b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator/(Strong a, T2 b) { return Strong(a.get() / b); } -template +template constexpr decltype(auto) operator/(Strong a, Strong b) { return Strong(a.get() / b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator^(T1 a, Strong b) { return Strong(a ^ b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator^(Strong a, T2 b) { return Strong(a.get() ^ b); } -template +template constexpr decltype(auto) operator^(Strong a, Strong b) { return Strong(a.get() ^ b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator&(T1 a, Strong b) { return Strong(a & b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator&(Strong a, T2 b) { return Strong(a.get() & b); } -template +template constexpr decltype(auto) operator&(Strong a, Strong b) { return Strong(a.get() & b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator|(T1 a, Strong b) { return Strong(a | b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator|(Strong a, T2 b) { return Strong(a.get() | b); } -template +template constexpr decltype(auto) operator|(Strong a, Strong b) { return Strong(a.get() | b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator>>(T1 a, Strong b) { return Strong(a >> b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator>>(Strong a, T2 b) { return Strong(a.get() >> b); } -template +template constexpr decltype(auto) operator>>(Strong a, Strong b) { return Strong(a.get() >> b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator<<(T1 a, Strong b) { return Strong(a << b.get()); } -template +template requires(detail::has_capability) constexpr decltype(auto) operator<<(Strong a, T2 b) { return Strong(a.get() << b); } -template +template constexpr decltype(auto) operator<<(Strong a, Strong b) { return Strong(a.get() << b.get()); } -template +template requires(detail::has_capability) constexpr auto operator+=(Strong& a, T2 b) { a.get() += b; return a; } -template +template constexpr auto operator+=(Strong& a, Strong b) { a.get() += b.get(); return a; } -template +template requires(detail::has_capability) constexpr auto operator-=(Strong& a, T2 b) { a.get() -= b; return a; } -template +template constexpr auto operator-=(Strong& a, Strong b) { a.get() -= b.get(); return a; } -template +template requires(detail::has_capability) constexpr auto operator*=(Strong& a, T2 b) { a.get() *= b; return a; } -template +template constexpr auto operator*=(Strong& a, Strong b) { a.get() *= b.get(); return a; } -template +template requires(detail::has_capability) constexpr auto operator/=(Strong& a, T2 b) { a.get() /= b; return a; } -template +template constexpr auto operator/=(Strong& a, Strong b) { a.get() /= b.get(); return a; } -template +template requires(detail::has_capability) constexpr auto operator^=(Strong& a, T2 b) { a.get() ^= b; return a; } -template +template constexpr auto operator^=(Strong& a, Strong b) { a.get() ^= b.get(); return a; } -template +template requires(detail::has_capability) constexpr auto operator&=(Strong& a, T2 b) { a.get() &= b; return a; } -template +template constexpr auto operator&=(Strong& a, Strong b) { a.get() &= b.get(); return a; } -template +template requires(detail::has_capability) constexpr auto operator|=(Strong& a, T2 b) { a.get() |= b; return a; } -template +template constexpr auto operator|=(Strong& a, Strong b) { a.get() |= b.get(); return a; } -template +template requires(detail::has_capability) constexpr auto operator>>=(Strong& a, T2 b) { a.get() >>= b; return a; } -template +template constexpr auto operator>>=(Strong& a, Strong b) { a.get() >>= b.get(); return a; } -template +template requires(detail::has_capability) constexpr auto operator<<=(Strong& a, T2 b) { a.get() <<= b; return a; } -template +template constexpr auto operator<<=(Strong& a, Strong b) { a.get() <<= b.get(); return a; } -template +template constexpr auto operator++(Strong& a, int) { auto tmp = a; ++a.get(); return tmp; } -template +template constexpr auto operator++(Strong& a) { ++a.get(); return a; } -template +template constexpr auto operator--(Strong& a, int) { auto tmp = a; --a.get(); return tmp; } -template +template constexpr auto operator--(Strong& a) { --a.get(); return a; diff --git a/src/scripts/bench.py b/src/scripts/bench.py index 09b9200e009..250f0ec83be 100755 --- a/src/scripts/bench.py +++ b/src/scripts/bench.py @@ -5,12 +5,9 @@ (C) 2017,2022 Jack Lloyd 2023 René Meusel, Rohde & Schwarz Cybersecurity GmbH - 2023 René Fischer + 2023,2024 René Fischer Botan is released under the Simplified BSD License (see license.txt) - -TODO - - Output pretty graphs with matplotlib """ import logging @@ -20,6 +17,12 @@ import subprocess import re import json +import math +import platform +from datetime import datetime +import shutil +import numpy as np +import matplotlib.pyplot as plt def setup_logging(options): if options.verbose: @@ -276,6 +279,30 @@ def result_string(self): self.algo, k, v['botan'], v['openssl'], winner, ratio) return out + def graphs(self): + buf_sizes = self.results.keys() + x = np.arange(len(buf_sizes)) + width = 0.4 + multiplier = 0 + + fig, ax = plt.subplots(layout='constrained') + + for lib in ['Botan', 'OpenSSL']: + offset = width * multiplier + results = [math.ceil(x[lib.lower()]/(1024*1024)) for x in self.results.values()] + rects = ax.bar(x + offset, results, width, label=lib) + ax.bar_label(rects, padding=5) + multiplier += 1 + + ax.set_xlabel('Buffer size (bytes)') + ax.set_ylabel('MiB/sec') + ax.set_title(self.algo) + ax.set_xticks(x + width, buf_sizes) + ax.legend(loc='upper left', ncols=2) + + return {self.algo: fig} + + class SignatureBenchmarkResult: def __init__(self, algo, sizes, openssl_results, botan_results): self.algo = algo @@ -314,6 +341,36 @@ def result_string(self): return out + def graphs(self): + key_sizes = self.results.keys() + x = np.arange(len(key_sizes)) + width = 0.4 + + graphs = {} + + for op in ['sign', 'verify']: + fig, ax = plt.subplots(layout='constrained') + multiplier = 0 + + for lib in ['Botan', 'OpenSSL']: + offset = width * multiplier + results = [x[lib.lower()][op] for x in self.results.values()] + rects = ax.bar(x + offset, results, width, label=lib) + ax.bar_label(rects, padding=5) + multiplier += 1 + + # Add some text for labels, title and custom x-axis tick labels, etc. + ax.set_xlabel('Key size (bits)') + ax.set_ylabel('Ops/sec') + ax.set_title(self.algo + ' ' + op) + ax.set_xticks(x + width, key_sizes) + ax.legend(loc='upper right', ncols=2) + + graphs.update({self.algo + '_' + op: fig}) + + return graphs + + class KeyAgreementBenchmarkResult: def __init__(self, algo, sizes, openssl_results, botan_results): self.algo = algo @@ -347,6 +404,30 @@ def result_string(self): self.algo, k, v['botan'], v['openssl'], winner, ratio) return out + def graphs(self): + key_sizes = self.results.keys() + x = np.arange(len(key_sizes)) + width = 0.4 + multiplier = 0 + + fig, ax = plt.subplots(layout='constrained') + + for lib in ['Botan', 'OpenSSL']: + offset = width * multiplier + results = [x[lib.lower()] for x in self.results.values()] + rects = ax.bar(x + offset, results, width, label=lib) + ax.bar_label(rects, padding=5) + multiplier += 1 + + ax.set_xlabel('Key size (bits)') + ax.set_ylabel('Key agreements/sec') + ax.set_title(self.algo) + ax.set_xticks(x + width, key_sizes) + ax.legend(loc='upper right', ncols=2) + + return {self.algo: fig} + + def bench_algo(openssl, botan, algo): openssl_results = run_openssl_bench(openssl, algo) @@ -380,6 +461,61 @@ def bench_key_agreement_algo(openssl, botan, algo): return KeyAgreementBenchmarkResult(algo, sorted(kszs_ossl.intersection(kszs_botan)), openssl_results, botan_results) +def gen_html_report(botan_version, openssl_version, cmdline): + html = """ + + + Botan Benchmark HTML Report + + +

Botan Benchmark HTML Report

+

Environment

+

Date: {date}

+

Operating System: {os_info}

+

Botan version: {botan_version}

+
+ Build Info +

{build_h}

+
+

OpenSSL version: {openssl_version}

+

Invocation: +

{cmdline}
+

+ +

Results

""" + + for img in sorted(os.listdir('bench_html/img')): + html += '' % img + + html += """ + + + """ + + os_info = platform.system() + os_info += ' ' + platform.release() + os_info += ' (' + os_info += platform.machine() + os_info += ')' + + include_path = re.search(r'-I(.*)', run_command(['botan', 'config', 'cflags']).strip()).group(1) + build_h = 'N/A' + + try: + with open(os.path.join(include_path, 'botan/build.h'), 'r', encoding='utf-8') as f: + build_h = f.read() + except Exception as e: + logging.warning("Unable to read build.h: %s", e) + + with open('bench_html/index.html', 'w', encoding='utf-8') as f: + f.write(html.format(date=datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3], + os_info=os_info, + botan_version=botan_version, + build_h=build_h, + openssl_version=openssl_version, + cmdline=cmdline)) + + def main(args=None): if args is None: args = sys.argv @@ -390,6 +526,8 @@ def main(args=None): parser.add_option('--verbose', action='store_true', default=False, help="be noisy") parser.add_option('--quiet', action='store_true', default=False, help="be very quiet") + parser.add_option('--html-report', action='store_true', default=False, help="create HTML report") + parser.add_option('--openssl-cli', metavar='PATH', default='/usr/bin/openssl', help='Path to openssl binary (default %default)') @@ -416,33 +554,54 @@ def main(args=None): logging.info("Comparing Botan %s with OpenSSL %s", botan_version, openssl_version) + if options.html_report: + if os.path.exists('bench_html'): + shutil.rmtree('bench_html') + + os.mkdir('bench_html') + os.mkdir('bench_html/img') + + graphs = {} + if len(args) > 1: algos = args[1:] for algo in algos: if algo in EVP_MAP: result = bench_algo(openssl, botan, algo) + graphs.update(result.graphs()) print(result.result_string()) elif algo in SIGNATURE_EVP_MAP: result = bench_signature_algo(openssl, botan, algo) + graphs.update(result.graphs()) print(result.result_string()) elif algo in KEY_AGREEMENT_EVP_MAP: result = bench_key_agreement_algo(openssl, botan, algo) + graphs.update(result.graphs()) print(result.result_string()) else: logging.error("Unknown algorithm '%s'", algo) else: for algo in sorted(EVP_MAP): result = bench_algo(openssl, botan, algo) + graphs.update(result.graphs()) print(result.result_string()) for algo in sorted(SIGNATURE_EVP_MAP): result = bench_signature_algo(openssl, botan, algo) + graphs.update(result.graphs()) print(result.result_string()) for algo in sorted(KEY_AGREEMENT_EVP_MAP): result = bench_key_agreement_algo(openssl, botan, algo) + graphs.update(result.graphs()) print(result.result_string()) + if options.html_report: + for title, graph in graphs.items(): + graph.savefig('bench_html/img/' + title.replace('/', '--') + ".png") + + gen_html_report(botan_version, openssl_version, ' '.join(sys.argv)) + return 0 if __name__ == '__main__': diff --git a/src/scripts/ci/setup_gh_actions.sh b/src/scripts/ci/setup_gh_actions.sh index 857b5f19b3e..c6346080faa 100755 --- a/src/scripts/ci/setup_gh_actions.sh +++ b/src/scripts/ci/setup_gh_actions.sh @@ -71,7 +71,7 @@ if type -p "apt-get"; then sudo apt-get -qq install emscripten elif [ "$TARGET" = "lint" ]; then - sudo apt-get -qq install pylint + sudo apt-get -qq install pylint python3-matplotlib elif [ "$TARGET" = "coverage" ] || [ "$TARGET" = "sanitizer" ]; then if [ "$TARGET" = "coverage" ]; then diff --git a/src/tests/data/bn/gcd.vec b/src/tests/data/bn/gcd.vec index f3d0acb2084..333ec09de59 100644 --- a/src/tests/data/bn/gcd.vec +++ b/src/tests/data/bn/gcd.vec @@ -724,3 +724,8 @@ GCD = 1099511627776 X = 37 Y = 65427 GCD = 1 + +# OSS-Fuzz 66162 +X = 289 +Y = 58403 +GCD = 1 diff --git a/src/tests/runner/test_runner.cpp b/src/tests/runner/test_runner.cpp index f461888fcad..7888330db57 100644 --- a/src/tests/runner/test_runner.cpp +++ b/src/tests/runner/test_runner.cpp @@ -28,50 +28,6 @@ Test_Runner::Test_Runner(std::ostream& out) : m_output(out) {} Test_Runner::~Test_Runner() = default; -namespace { - -/* -* This is a fast, simple, deterministic PRNG that's used for running -* the tests. It is not intended to be cryptographically secure. -*/ -class Testsuite_RNG final : public Botan::RandomNumberGenerator { - public: - std::string name() const override { return "Testsuite_RNG"; } - - void clear() override { m_x = 0; } - - bool accepts_input() const override { return true; } - - bool is_seeded() const override { return true; } - - void fill_bytes_with_input(std::span output, std::span input) override { - for(const auto byte : input) { - mix(byte); - } - - for(auto& byte : output) { - byte = mix(); - } - } - - Testsuite_RNG(const std::vector& seed, uint64_t test_counter) { - m_x = ~test_counter; - add_entropy(seed.data(), seed.size()); - } - - private: - uint8_t mix(uint8_t input = 0) { - m_x ^= input; - m_x *= 0xF2E16957; - m_x += 0xE50B590F; - return static_cast(m_x >> 27); - } - - uint64_t m_x; -}; - -} // namespace - bool Test_Runner::run(const Test_Options& opts) { if(!opts.no_stdout()) { m_reporters.emplace_back(std::make_unique(opts, output())); @@ -129,7 +85,7 @@ bool Test_Runner::run(const Test_Options& opts) { Botan_Tests::Test::set_test_options(opts); for(size_t i = 0; i != opts.test_runs(); ++i) { - Botan_Tests::Test::set_test_rng(std::make_shared(seed, i)); + Botan_Tests::Test::set_test_rng_seed(seed, i); for(const auto& reporter : m_reporters) { reporter->next_test_run(); diff --git a/src/tests/test_aead.cpp b/src/tests/test_aead.cpp index 49e27304b3c..cc0ff27725a 100644 --- a/src/tests/test_aead.cpp +++ b/src/tests/test_aead.cpp @@ -27,7 +27,8 @@ class AEAD_Tests final : public Text_Based_Test { const std::vector& input, const std::vector& expected, const std::vector& ad, - const std::string& algo) { + const std::string& algo, + Botan::RandomNumberGenerator& rng) { const bool is_siv = algo.find("/SIV") != std::string::npos; Test::Result result(algo); @@ -39,7 +40,7 @@ class AEAD_Tests final : public Text_Based_Test { result.confirm("AEAD name is not empty", !enc->name().empty()); result.confirm("AEAD default nonce size is accepted", enc->valid_nonce_length(enc->default_nonce_length())); - Botan::secure_vector garbage = Test::rng().random_vec(enc->update_granularity()); + Botan::secure_vector garbage = rng.random_vec(enc->update_granularity()); if(is_siv == false) { result.test_throws("Unkeyed object throws for encrypt", [&]() { enc->update(garbage); }); @@ -64,8 +65,8 @@ class AEAD_Tests final : public Text_Based_Test { result.test_throws("Cannot process data until nonce is set (enc)", [&]() { enc->finish(garbage); }); } - enc->set_associated_data(mutate_vec(ad)); - enc->start(mutate_vec(nonce)); + enc->set_associated_data(mutate_vec(ad, rng)); + enc->start(mutate_vec(nonce, rng)); enc->update(garbage); // reset message specific state @@ -184,7 +185,8 @@ class AEAD_Tests final : public Text_Based_Test { const std::vector& input, const std::vector& expected, const std::vector& ad, - const std::string& algo) { + const std::string& algo, + Botan::RandomNumberGenerator& rng) { const bool is_siv = algo.find("/SIV") != std::string::npos; Test::Result result(algo); @@ -193,7 +195,7 @@ class AEAD_Tests final : public Text_Based_Test { result.test_eq("AEAD decrypt output_length is correct", dec->output_length(input.size()), expected.size()); - Botan::secure_vector garbage = Test::rng().random_vec(dec->update_granularity()); + Botan::secure_vector garbage = rng.random_vec(dec->update_granularity()); if(is_siv == false) { result.test_throws("Unkeyed object throws for decrypt", [&]() { dec->update(garbage); }); @@ -211,14 +213,14 @@ class AEAD_Tests final : public Text_Based_Test { result.test_eq("key is not set", dec->has_keying_material(), false); dec->set_key(key); result.test_eq("key is set", dec->has_keying_material(), true); - dec->set_associated_data(mutate_vec(ad)); + dec->set_associated_data(mutate_vec(ad, rng)); if(is_siv == false) { result.test_throws("Cannot process data until nonce is set (dec)", [&]() { dec->update(garbage); }); result.test_throws("Cannot process data until nonce is set (dec)", [&]() { dec->finish(garbage); }); } - dec->start(mutate_vec(nonce)); + dec->start(mutate_vec(nonce, rng)); dec->update(garbage); @@ -313,7 +315,7 @@ class AEAD_Tests final : public Text_Based_Test { } // test decryption with modified ciphertext - const std::vector mutated_input = mutate_vec(input, true); + const std::vector mutated_input = mutate_vec(input, rng, true); buf.assign(mutated_input.begin(), mutated_input.end()); dec->reset(); @@ -333,7 +335,7 @@ class AEAD_Tests final : public Text_Based_Test { // test decryption with modified nonce if(!nonce.empty()) { buf.assign(input.begin(), input.end()); - std::vector bad_nonce = mutate_vec(nonce); + std::vector bad_nonce = mutate_vec(nonce, rng); dec->reset(); dec->set_associated_data(ad); @@ -350,7 +352,7 @@ class AEAD_Tests final : public Text_Based_Test { } // test decryption with modified associated_data - const std::vector bad_ad = mutate_vec(ad, true); + const std::vector bad_ad = mutate_vec(ad, rng, true); dec->reset(); dec->set_associated_data(bad_ad); @@ -426,10 +428,10 @@ class AEAD_Tests final : public Text_Based_Test { enc->ideal_granularity() % enc->update_granularity() == 0); // test enc - result.merge(test_enc(key, nonce, input, expected, ad, algo)); + result.merge(test_enc(key, nonce, input, expected, ad, algo, this->rng())); // test dec - result.merge(test_dec(key, nonce, expected, input, ad, algo)); + result.merge(test_dec(key, nonce, expected, input, ad, algo, this->rng())); return result; } diff --git a/src/tests/test_bigint.cpp b/src/tests/test_bigint.cpp index 5da90d8406b..301c9f77042 100644 --- a/src/tests/test_bigint.cpp +++ b/src/tests/test_bigint.cpp @@ -1,5 +1,6 @@ /* * (C) 2009,2015,2016 Jack Lloyd +* (C) 2024 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -12,6 +13,8 @@ #include #include #include + #include + #include #include #include #endif @@ -78,36 +81,36 @@ class BigInt_Unit_Tests final : public Test { static Test::Result test_random_prime() { Test::Result result("BigInt prime generation"); - result.test_throws("Invalid bit size", "random_prime: Can't make a prime of 0 bits", []() { - Botan::random_prime(Test::rng(), 0); - }); - result.test_throws("Invalid bit size", "random_prime: Can't make a prime of 1 bits", []() { - Botan::random_prime(Test::rng(), 1); - }); - result.test_throws("Invalid arg", "random_prime Invalid value for equiv/modulo", []() { - Botan::random_prime(Test::rng(), 2, 1, 0, 2); + auto rng = Test::new_rng("random_prime"); + + result.test_throws( + "Invalid bit size", "random_prime: Can't make a prime of 0 bits", [&]() { Botan::random_prime(*rng, 0); }); + result.test_throws( + "Invalid bit size", "random_prime: Can't make a prime of 1 bits", [&]() { Botan::random_prime(*rng, 1); }); + result.test_throws("Invalid arg", "random_prime Invalid value for equiv/modulo", [&]() { + Botan::random_prime(*rng, 2, 1, 0, 2); }); - BigInt p = Botan::random_prime(Test::rng(), 2); + BigInt p = Botan::random_prime(*rng, 2); result.confirm("Only two 2-bit primes", p == 2 || p == 3); - p = Botan::random_prime(Test::rng(), 3); + p = Botan::random_prime(*rng, 3); result.confirm("Only two 3-bit primes", p == 5 || p == 7); - p = Botan::random_prime(Test::rng(), 4); + p = Botan::random_prime(*rng, 4); result.confirm("Only two 4-bit primes", p == 11 || p == 13); for(size_t bits = 5; bits <= 32; ++bits) { - p = Botan::random_prime(Test::rng(), bits); + p = Botan::random_prime(*rng, bits); result.test_eq("Expected bit size", p.bits(), bits); - result.test_eq("P is prime", Botan::is_prime(p, Test::rng()), true); + result.test_eq("P is prime", Botan::is_prime(p, *rng), true); } const size_t safe_prime_bits = 65; - const BigInt safe_prime = Botan::random_safe_prime(Test::rng(), safe_prime_bits); + const BigInt safe_prime = Botan::random_safe_prime(*rng, safe_prime_bits); result.test_eq("Safe prime size", safe_prime.bits(), safe_prime_bits); - result.confirm("P is prime", Botan::is_prime(safe_prime, Test::rng())); - result.confirm("(P-1)/2 is prime", Botan::is_prime((safe_prime - 1) / 2, Test::rng())); + result.confirm("P is prime", Botan::is_prime(safe_prime, *rng)); + result.confirm("(P-1)/2 is prime", Botan::is_prime((safe_prime - 1) / 2, *rng)); return result; } @@ -140,7 +143,9 @@ class BigInt_Unit_Tests final : public Test { const size_t rbits = 1024; - const Botan::BigInt r(Test::rng(), rbits); + auto rng = Test::new_rng("get_substring"); + + const Botan::BigInt r(*rng, rbits); for(size_t wlen = 1; wlen <= 32; ++wlen) { for(size_t offset = 0; offset != rbits + 64; ++offset) { @@ -536,6 +541,28 @@ class BigInt_Rshift_Test final : public Text_Based_Test { BOTAN_REGISTER_TEST("math", "bn_rshift", BigInt_Rshift_Test); +Test::Result test_const_time_left_shift() { + Test::Result result("BigInt const time shift"); + const size_t bits = Test::run_long_tests() ? 2 * 4096 : 2048; + + Botan::BigInt a = Botan::BigInt::with_capacity(bits / sizeof(Botan::word)); + for(size_t i = 0; i < bits; ++i) { + a.set_bit(i); + } + + for(size_t i = 0; i < bits; ++i) { + auto ct = a; + auto chk = a; + ct.const_time_poison(); + ct.ct_shift_left(i); + ct.const_time_unpoison(); + chk <<= i; + result.test_eq(Botan::fmt("ct << {}", i), ct, chk); + } + + return result; +} + class BigInt_Powmod_Test final : public Text_Based_Test { public: BigInt_Powmod_Test() : Text_Based_Test("bn/powmod.vec", "Base,Exponent,Modulus,Output") {} @@ -568,7 +595,7 @@ class BigInt_IsPrime_Test final : public Text_Based_Test { const bool is_prime = (header == "Prime"); Test::Result result("BigInt Test " + header); - result.test_eq("is_prime", Botan::is_prime(value, Test::rng()), is_prime); + result.test_eq("is_prime", Botan::is_prime(value, this->rng()), is_prime); return result; } @@ -691,7 +718,7 @@ class Lucas_Primality_Test final : public Test { for(uint32_t i = 3; i <= lucas_max; i += 2) { Botan::Modular_Reducer mod_i(i); const bool passes_lucas = Botan::is_lucas_probable_prime(i, mod_i); - const bool is_prime = Botan::is_prime(i, Test::rng()); + const bool is_prime = Botan::is_prime(i, this->rng()); const bool is_lucas_pp = (is_prime == false && passes_lucas == true); @@ -732,7 +759,7 @@ class DSA_ParamGen_Test final : public Text_Based_Test { try { Botan::BigInt gen_P, gen_Q; - if(Botan::generate_dsa_primes(Test::rng(), gen_P, gen_Q, p_bits, q_bits, seed, offset)) { + if(Botan::generate_dsa_primes(this->rng(), gen_P, gen_Q, p_bits, q_bits, seed, offset)) { result.test_eq("P", gen_P, exp_P); result.test_eq("Q", gen_Q, exp_Q); } else { @@ -746,6 +773,60 @@ class DSA_ParamGen_Test final : public Text_Based_Test { BOTAN_REGISTER_TEST("math", "dsa_param", DSA_ParamGen_Test); +std::vector test_bigint_serialization() { + auto rng = Test::new_rng("test_bigint_serialization"); + + return { + Botan_Tests::CHECK("BigInt binary serialization", + [](Test::Result& res) { + Botan::BigInt a(0x1234567890ABCDEF); + auto enc = Botan::BigInt::encode(a); + res.test_eq("BigInt::encode()", enc, Botan::hex_decode("1234567890ABCDEF")); + + auto enc_locked = Botan::BigInt::encode_locked(a); + res.test_eq( + "BigInt::encode_locked()", enc_locked, Botan::hex_decode_locked("1234567890ABCDEF")); + std::vector enc2(a.bytes()); + a.binary_encode(enc2.data(), enc2.size()); + res.test_eq("BigInt::binary_encode", enc2, Botan::hex_decode("1234567890ABCDEF")); + }), + + Botan_Tests::CHECK("BigInt truncated/padded binary serialization", + [&](Test::Result& res) { + Botan::BigInt a(0xFEDCBA9876543210); + + std::vector enc1(a.bytes() - 1); + a.binary_encode(enc1.data(), enc1.size()); + res.test_eq("BigInt::binary_encode", enc1, Botan::hex_decode("DCBA9876543210")); + + std::vector enc2(a.bytes() - 3); + a.binary_encode(enc2.data(), enc2.size()); + res.test_eq("BigInt::binary_encode", enc2, Botan::hex_decode("9876543210")); + + std::vector enc3(a.bytes() + 1); + a.binary_encode(enc3.data(), enc3.size()); + res.test_eq("BigInt::binary_encode", enc3, Botan::hex_decode("00FEDCBA9876543210")); + + // make sure that the padding is actually written + std::vector enc4(a.bytes() + 3); + rng->randomize(enc4); + a.binary_encode(enc4.data(), enc4.size()); + res.test_eq("BigInt::binary_encode", enc4, Botan::hex_decode("000000FEDCBA9876543210")); + + Botan::BigInt b(Botan::hex_decode("FEDCBA9876543210BAADC0FFEE")); + + std::vector enc5(b.bytes() + 12); + rng->randomize(enc5); + b.binary_encode(enc5.data(), enc5.size()); + res.test_eq("BigInt::binary_encode", + enc5, + Botan::hex_decode("000000000000000000000000FEDCBA9876543210BAADC0FFEE")); + }), + }; +} + +BOTAN_REGISTER_TEST_FN("math", "bignum_auxiliary", test_const_time_left_shift, test_bigint_serialization); + #endif } // namespace diff --git a/src/tests/test_block.cpp b/src/tests/test_block.cpp index eb20f945fdd..462bf3fc41b 100644 --- a/src/tests/test_block.cpp +++ b/src/tests/test_block.cpp @@ -78,8 +78,8 @@ class Block_Cipher_Tests final : public Text_Based_Test { } // Test to make sure clear() resets what we need it to - cipher->set_key(Test::rng().random_vec(cipher->key_spec().maximum_keylength())); - Botan::secure_vector garbage = Test::rng().random_vec(cipher->block_size()); + cipher->set_key(this->rng().random_vec(cipher->key_spec().maximum_keylength())); + Botan::secure_vector garbage = this->rng().random_vec(cipher->block_size()); cipher->encrypt(garbage); cipher->clear(); @@ -109,7 +109,7 @@ class Block_Cipher_Tests final : public Text_Based_Test { auto clone = cipher->new_object(); result.confirm("Clone has different pointer", cipher.get() != clone.get()); result.test_eq("Clone has same name", cipher->name(), clone->name()); - clone->set_key(Test::rng().random_vec(cipher->maximum_keylength())); + clone->set_key(this->rng().random_vec(cipher->maximum_keylength())); // have called set_key on clone: process input values std::vector buf = input; diff --git a/src/tests/test_c25519.cpp b/src/tests/test_c25519.cpp index c2243d21880..555b01a6360 100644 --- a/src/tests/test_c25519.cpp +++ b/src/tests/test_c25519.cpp @@ -65,8 +65,8 @@ class Curve25519_Roundtrip_Test final : public Test { for(size_t i = 0; i < 10; ++i) { Test::Result result("Curve25519 roundtrip"); - Botan::Curve25519_PrivateKey a_priv_gen(Test::rng()); - Botan::Curve25519_PrivateKey b_priv_gen(Test::rng()); + Botan::Curve25519_PrivateKey a_priv_gen(this->rng()); + Botan::Curve25519_PrivateKey b_priv_gen(this->rng()); #if defined(BOTAN_HAS_PKCS5_PBES2) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && \ defined(BOTAN_HAS_SHA2_32) @@ -75,8 +75,8 @@ class Curve25519_Roundtrip_Test final : public Test { const std::string a_pass = "alice pass"; const std::string b_pass = "bob pass"; const auto pbe_time = std::chrono::milliseconds(1); - const std::string a_priv_pem = Botan::PKCS8::PEM_encode(a_priv_gen, Test::rng(), a_pass, pbe_time); - const std::string b_priv_pem = Botan::PKCS8::PEM_encode(b_priv_gen, Test::rng(), b_pass, pbe_time); + const std::string a_priv_pem = Botan::PKCS8::PEM_encode(a_priv_gen, this->rng(), a_pass, pbe_time); + const std::string b_priv_pem = Botan::PKCS8::PEM_encode(b_priv_gen, this->rng(), b_pass, pbe_time); // Reload back into memory Botan::DataSource_Memory a_priv_ds(a_priv_pem); @@ -110,8 +110,8 @@ class Curve25519_Roundtrip_Test final : public Test { Botan::Curve25519_PublicKey* b_pub_key = dynamic_cast(b_pub.get()); if(a_pub_key && b_pub_key) { - Botan::PK_Key_Agreement a_ka(*a_priv, Test::rng(), "Raw"); - Botan::PK_Key_Agreement b_ka(*b_priv, Test::rng(), "Raw"); + Botan::PK_Key_Agreement a_ka(*a_priv, this->rng(), "Raw"); + Botan::PK_Key_Agreement b_ka(*b_priv, this->rng(), "Raw"); Botan::SymmetricKey a_key = a_ka.derive_key(32, b_pub_key->public_value()); Botan::SymmetricKey b_key = b_ka.derive_key(32, a_pub_key->public_value()); diff --git a/src/tests/test_certstor.cpp b/src/tests/test_certstor.cpp index edf397b3866..47a6d3bda99 100644 --- a/src/tests/test_certstor.cpp +++ b/src/tests/test_certstor.cpp @@ -58,10 +58,10 @@ Test::Result test_certstor_sqlite3_insert_find_remove_test(const std::vector(rng.random_vec(8).data()), 8); + auto rng = Test::new_rng(__func__); + const std::string passwd(reinterpret_cast(rng->random_vec(8).data()), 8); // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html) - Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng); + Botan::Certificate_Store_In_SQLite store(":memory:", passwd, *rng); for(const auto& a : certsandkeys) { store.insert_key(a.certificate(), a.private_key()); @@ -127,10 +127,10 @@ Test::Result test_certstor_sqlite3_insert_find_remove_test(const std::vector& certsandkeys) { Test::Result result("Certificate Store SQLITE3 - CRL"); try { - auto& rng = Test::rng(); - const std::string passwd(reinterpret_cast(rng.random_vec(8).data()), 8); + auto rng = Test::new_rng(__func__); + const std::string passwd(reinterpret_cast(rng->random_vec(8).data()), 8); // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html) - Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng); + Botan::Certificate_Store_In_SQLite store(":memory:", passwd, *rng); for(const auto& a : certsandkeys) { store.insert_cert(a.certificate()); @@ -185,10 +185,10 @@ Test::Result test_certstor_sqlite3_crl_test(const std::vector Test::Result test_certstor_sqlite3_all_subjects_test(const std::vector& certsandkeys) { Test::Result result("Certificate Store SQLITE3 - All subjects"); try { - auto& rng = Test::rng(); - const std::string passwd(reinterpret_cast(rng.random_vec(8).data()), 8); + auto rng = Test::new_rng(__func__); + const std::string passwd(reinterpret_cast(rng->random_vec(8).data()), 8); // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html) - Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng); + Botan::Certificate_Store_In_SQLite store(":memory:", passwd, *rng); for(const auto& a : certsandkeys) { store.insert_cert(a.certificate()); @@ -217,10 +217,10 @@ Test::Result test_certstor_sqlite3_all_subjects_test(const std::vector& certsandkeys) { Test::Result result("Certificate Store SQLITE3 - Find all certs"); try { - auto& rng = Test::rng(); - const std::string passwd(reinterpret_cast(rng.random_vec(8).data()), 8); + auto rng = Test::new_rng(__func__); + const std::string passwd(reinterpret_cast(rng->random_vec(8).data()), 8); // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html) - Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng); + Botan::Certificate_Store_In_SQLite store(":memory:", passwd, *rng); for(const auto& a : certsandkeys) { store.insert_cert(a.certificate()); diff --git a/src/tests/test_compression.cpp b/src/tests/test_compression.cpp index 633cef3dfbf..43a296855ac 100644 --- a/src/tests/test_compression.cpp +++ b/src/tests/test_compression.cpp @@ -76,7 +76,7 @@ class Compression_Tests final : public Test { const Botan::secure_vector empty; const Botan::secure_vector all_zeros(text_len, 0); - const Botan::secure_vector random_binary = Test::rng().random_vec(text_len); + const Botan::secure_vector random_binary = this->rng().random_vec(text_len); const Botan::secure_vector short_text = {'f', 'o', 'o', '\n'}; const uint8_t* textb = reinterpret_cast(COMPRESSION_TEST_TEXT); @@ -96,11 +96,14 @@ class Compression_Tests final : public Test { result.test_gte("Empty input L1 compresses to non-empty output", c1_e, 1); result.test_gte("Empty input L9 compresses to non-empty output", c9_e, 1); - result.test_gte("Level 9 compresses empty at least as well as level 1", c1_e, c9_e); - result.test_gte("Level 9 compresses zeros at least as well as level 1", c1_z, c9_z); - result.test_gte("Level 9 compresses random at least as well as level 1", c1_r, c9_r); - result.test_gte("Level 9 compresses text at least as well as level 1", c1_t, c9_t); - result.test_gte("Level 9 compresses short text at least as well as level 1", c1_s, c9_s); + // We assume that Level 9 is better than Level 1, but this is not + // guaranteed (see GitHub #3896). Hence, we assert that level 9 + // it is at most 10% worse than level 1. + result.test_gte("Level 9 compresses empty at least as well as level 1", c1_e + (c1_e / 10), c9_e); + result.test_gte("Level 9 compresses zeros at least as well as level 1", c1_z + (c1_z / 10), c9_z); + result.test_gte("Level 9 compresses random at least as well as level 1", c1_r + (c1_r / 10), c9_r); + result.test_gte("Level 9 compresses text at least as well as level 1", c1_t + (c1_t / 10), c9_t); + result.test_gte("Level 9 compresses short text at least as well as level 1", c1_s + (c1_s / 10), c9_s); result.test_lt("Zeros compresses much better than text", c1_z / 8, c1_t); result.test_lt("Text compresses much better than random", c1_t / 2, c1_r); diff --git a/src/tests/test_cryptobox.cpp b/src/tests/test_cryptobox.cpp index 5496bd88411..c89cd006506 100644 --- a/src/tests/test_cryptobox.cpp +++ b/src/tests/test_cryptobox.cpp @@ -47,7 +47,7 @@ class Cryptobox_KAT final : public Text_Based_Test { // Now corrupt a bit and ensure it fails try { - const std::vector corrupted = Test::mutate_vec(expected); + const std::vector corrupted = Test::mutate_vec(expected, this->rng()); const std::string corrupted_pem = Botan::PEM_Code::encode(corrupted, "BOTAN CRYPTOBOX MESSAGE"); Botan::CryptoBox::decrypt(corrupted_pem, password); diff --git a/src/tests/test_dh.cpp b/src/tests/test_dh.cpp index 2ae2aaed95f..a8fdd1e57ff 100644 --- a/src/tests/test_dh.cpp +++ b/src/tests/test_dh.cpp @@ -69,7 +69,7 @@ class Diffie_Hellman_KAT_Tests final : public PK_Key_Agreement_Test { const Botan::BigInt x("46205663093589612668746163860870963912226379131190812163519349848291472898748"); auto privkey = std::make_unique(group, x); - auto kas = std::make_unique(*privkey, rng(), "Raw"); + auto kas = std::make_unique(*privkey, this->rng(), "Raw"); result.test_throws("agreement input too big", "DH agreement - invalid key provided", [&kas]() { const BigInt too_big("584580020955360946586837552585233629614212007514394561597561641914945762794672"); @@ -102,7 +102,7 @@ class DH_Invalid_Key_Tests final : public Text_Based_Test { Botan::DL_Group group(p, q, g); auto key = std::make_unique(group, pubkey); - result.test_eq("public key fails check", key->check_key(Test::rng(), false), false); + result.test_eq("public key fails check", key->check_key(this->rng(), false), false); return result; } }; diff --git a/src/tests/test_dilithium.cpp b/src/tests/test_dilithium.cpp index f31291ca546..93e0aeb1309 100644 --- a/src/tests/test_dilithium.cpp +++ b/src/tests/test_dilithium.cpp @@ -67,7 +67,7 @@ class Dilithium_KAT_Tests : public Text_Based_Test { result.confirm("signature verifies", verifier.check_signature(signature.data(), signature.size())); // test validating incorrect wrong signagture - auto mutated_signature = Test::mutate_vec(signature); + auto mutated_signature = Test::mutate_vec(signature, this->rng()); result.confirm("invalid signature rejected", !verifier.check_signature(mutated_signature.data(), mutated_signature.size())); @@ -111,10 +111,12 @@ class DilithiumRoundtripTests final : public Test { static Test::Result run_roundtrip(const char* test_name, Botan::DilithiumMode mode, bool randomized) { Test::Result result(test_name); - auto sign = [randomized](const auto& private_key, const auto& msg) { + auto rng = Test::new_rng(test_name); + + auto sign = [randomized, &rng](const auto& private_key, const auto& msg) { const std::string param = (randomized) ? "Randomized" : "Deterministic"; - auto signer = Botan::PK_Signer(private_key, Test::rng(), param); - return signer.sign_message(msg, Test::rng()); + auto signer = Botan::PK_Signer(private_key, *rng, param); + return signer.sign_message(msg, *rng); }; auto verify = [](const auto& public_key, const auto& msg, const auto& signature) { @@ -126,7 +128,7 @@ class DilithiumRoundtripTests final : public Test { const std::string msg = "The quick brown fox jumps over the lazy dog."; const std::vector msgvec(msg.data(), msg.data() + msg.size()); - Botan::Dilithium_PrivateKey priv_key(Test::rng(), mode); + Botan::Dilithium_PrivateKey priv_key(*rng, mode); const Botan::Dilithium_PublicKey& pub_key = priv_key; const auto sig_before_codec = sign(priv_key, msgvec); diff --git a/src/tests/test_dl_group.cpp b/src/tests/test_dl_group.cpp index d4da8eba599..0a863823ee7 100644 --- a/src/tests/test_dl_group.cpp +++ b/src/tests/test_dl_group.cpp @@ -39,7 +39,8 @@ class DL_Group_Tests final : public Test { #if !defined(BOTAN_HAS_SANITIZER_UNDEFINED) result.test_throws("Bad generator param", "DL_Group unknown PrimeType", []() { auto invalid_type = static_cast(9); - Botan::DL_Group dl(Test::rng(), invalid_type, 1024); + Botan::Null_RNG null_rng; + Botan::DL_Group dl(null_rng, invalid_type, 1024); }); #endif @@ -86,7 +87,7 @@ class DL_Generate_Group_Tests final : public Test { result.start_timer(); - auto& rng = Test::rng(); + auto& rng = this->rng(); Botan::DL_Group dh1050(rng, Botan::DL_Group::Prime_Subgroup, 1050, 175); result.test_eq("DH p size", dh1050.get_p().bits(), 1050); @@ -192,7 +193,7 @@ class DL_Named_Group_Tests final : public Test { } if(group.p_bits() <= 1536 || Test::run_long_tests()) { - result.test_eq(name + " verifies", group.verify_group(Test::rng()), true); + result.test_eq(name + " verifies", group.verify_group(this->rng()), true); } } result.end_timer(); diff --git a/src/tests/test_dlies.cpp b/src/tests/test_dlies.cpp index 8ae82a9cc02..61ea1a0fa5f 100644 --- a/src/tests/test_dlies.cpp +++ b/src/tests/test_dlies.cpp @@ -74,9 +74,9 @@ class DLIES_KAT_Tests final : public Text_Based_Test { Botan::DH_PrivateKey to(domain, x2); Botan::DLIES_Encryptor encryptor( - from, Test::rng(), kdf->new_object(), std::move(enc), cipher_key_len, mac->new_object(), mac_key_len); + from, this->rng(), kdf->new_object(), std::move(enc), cipher_key_len, mac->new_object(), mac_key_len); Botan::DLIES_Decryptor decryptor( - to, Test::rng(), std::move(kdf), std::move(dec), cipher_key_len, std::move(mac), mac_key_len); + to, this->rng(), std::move(kdf), std::move(dec), cipher_key_len, std::move(mac), mac_key_len); if(!iv.empty()) { encryptor.set_initialization_vector(iv); @@ -85,10 +85,10 @@ class DLIES_KAT_Tests final : public Text_Based_Test { encryptor.set_other_key(to.public_value()); - result.test_eq("encryption", encryptor.encrypt(input, Test::rng()), expected); + result.test_eq("encryption", encryptor.encrypt(input, this->rng()), expected); result.test_eq("decryption", decryptor.decrypt(expected), input); - check_invalid_ciphertexts(result, decryptor, input, expected); + check_invalid_ciphertexts(result, decryptor, input, expected, this->rng()); return result; } @@ -107,8 +107,10 @@ Test::Result test_xor() { std::unique_ptr kdf; std::unique_ptr mac; - Botan::DH_PrivateKey alice(Test::rng(), Botan::DL_Group("modp/ietf/2048")); - Botan::DH_PrivateKey bob(Test::rng(), Botan::DL_Group("modp/ietf/2048")); + auto rng = Test::new_rng("dlies_xor"); + + Botan::DH_PrivateKey alice(*rng, Botan::DL_Group("modp/ietf/2048")); + Botan::DH_PrivateKey bob(*rng, Botan::DL_Group("modp/ietf/2048")); for(const auto& kfunc : kdfs) { kdf = Botan::KDF::create(kfunc); @@ -126,25 +128,25 @@ Test::Result test_xor() { continue; } - Botan::DLIES_Encryptor encryptor(alice, Test::rng(), kdf->new_object(), mac->new_object(), mac_key_len); + Botan::DLIES_Encryptor encryptor(alice, *rng, kdf->new_object(), mac->new_object(), mac_key_len); // negative test: other pub key not set - Botan::secure_vector plaintext = Test::rng().random_vec(32); + Botan::secure_vector plaintext = rng->random_vec(32); result.test_throws("encrypt not possible without setting other public key", - [&encryptor, &plaintext]() { encryptor.encrypt(plaintext, Test::rng()); }); + [&encryptor, &plaintext, &rng]() { encryptor.encrypt(plaintext, *rng); }); encryptor.set_other_key(bob.public_value()); - std::vector ciphertext = encryptor.encrypt(plaintext, Test::rng()); + std::vector ciphertext = encryptor.encrypt(plaintext, *rng); - Botan::DLIES_Decryptor decryptor(bob, Test::rng(), kdf->new_object(), mac->new_object(), mac_key_len); + Botan::DLIES_Decryptor decryptor(bob, *rng, kdf->new_object(), mac->new_object(), mac_key_len); // negative test: ciphertext too short result.test_throws("ciphertext too short", [&decryptor]() { decryptor.decrypt(std::vector(2)); }); result.test_eq("decryption", decryptor.decrypt(ciphertext), plaintext); - check_invalid_ciphertexts(result, decryptor, unlock(plaintext), ciphertext); + check_invalid_ciphertexts(result, decryptor, unlock(plaintext), ciphertext, *rng); } } diff --git a/src/tests/test_ec_group.cpp b/src/tests/test_ec_group.cpp index 1cc1877f327..2ddae62b5e1 100644 --- a/src/tests/test_ec_group.cpp +++ b/src/tests/test_ec_group.cpp @@ -103,7 +103,7 @@ std::vector ECC_Randomized_Tests::run() { Botan::EC_Group group(group_name); - const Botan::EC_Point pt = create_random_point(Test::rng(), group); + const Botan::EC_Point pt = create_random_point(this->rng(), group); const Botan::BigInt& group_order = group.get_order(); std::vector blind_ws; @@ -111,17 +111,17 @@ std::vector ECC_Randomized_Tests::run() { try { const size_t trials = (Test::run_long_tests() ? 10 : 3); for(size_t i = 0; i < trials; ++i) { - const Botan::BigInt a = Botan::BigInt::random_integer(Test::rng(), 2, group_order); - const Botan::BigInt b = Botan::BigInt::random_integer(Test::rng(), 2, group_order); + const Botan::BigInt a = Botan::BigInt::random_integer(this->rng(), 2, group_order); + const Botan::BigInt b = Botan::BigInt::random_integer(this->rng(), 2, group_order); const Botan::BigInt c = a + b; const Botan::EC_Point P = pt * a; const Botan::EC_Point Q = pt * b; const Botan::EC_Point R = pt * c; - Botan::EC_Point P1 = group.blinded_var_point_multiply(pt, a, Test::rng(), blind_ws); - Botan::EC_Point Q1 = group.blinded_var_point_multiply(pt, b, Test::rng(), blind_ws); - Botan::EC_Point R1 = group.blinded_var_point_multiply(pt, c, Test::rng(), blind_ws); + Botan::EC_Point P1 = group.blinded_var_point_multiply(pt, a, this->rng(), blind_ws); + Botan::EC_Point Q1 = group.blinded_var_point_multiply(pt, b, this->rng(), blind_ws); + Botan::EC_Point R1 = group.blinded_var_point_multiply(pt, c, this->rng(), blind_ws); Botan::EC_Point A1 = P + Q; Botan::EC_Point A2 = Q + P; @@ -203,13 +203,15 @@ class NIST_Curve_Reduction_Tests final : public Test { Botan::Modular_Reducer p_redc(p); Botan::secure_vector ws; + auto rng = Test::new_rng("random_redc " + prime_name); + Test::Result result("NIST " + prime_name + " reduction"); result.start_timer(); const size_t trials = (Test::run_long_tests() ? 128 : 16); for(size_t i = 0; i <= trials; ++i) { - const Botan::BigInt x = test_integer(Test::rng(), 2 * p_bits, p2); + const Botan::BigInt x = test_integer(*rng, 2 * p_bits, p2); // TODO: time and report all three approaches const Botan::BigInt v1 = x % p; @@ -244,7 +246,7 @@ class EC_Group_Tests : public Test { const Botan::EC_Group group(oid); result.confirm("EC_Group is known", !group.get_curve_oid().empty()); - result.confirm("EC_Group is considered valid", group.verify_group(Test::rng(), true)); + result.confirm("EC_Group is considered valid", group.verify_group(this->rng(), true)); result.confirm("EC_Group is not considered explict encoding", !group.used_explicit_encoding()); result.test_eq("EC_Group has correct bit size", group.get_p().bits(), group.get_p_bits()); @@ -291,13 +293,13 @@ class EC_Group_Tests : public Test { } // get a valid point - Botan::EC_Point p = group.get_base_point() * Test::rng().next_nonzero_byte(); + Botan::EC_Point p = group.get_base_point() * this->rng().next_nonzero_byte(); // get a copy Botan::EC_Point q = p; - p.randomize_repr(Test::rng()); - q.randomize_repr(Test::rng()); + p.randomize_repr(this->rng()); + q.randomize_repr(this->rng()); result.test_eq("affine x after copy", p.get_affine_x(), q.get_affine_x()); result.test_eq("affine y after copy", p.get_affine_y(), q.get_affine_y()); @@ -319,9 +321,9 @@ class EC_Group_Tests : public Test { } private: - static void test_ser_der(Test::Result& result, const Botan::EC_Group& group) { + void test_ser_der(Test::Result& result, const Botan::EC_Group& group) { // generate point - const Botan::EC_Point pt = create_random_point(Test::rng(), group); + const Botan::EC_Point pt = create_random_point(this->rng(), group); const Botan::EC_Point zero = group.zero_point(); for(auto scheme : {Botan::EC_Point_Format::Uncompressed, @@ -349,10 +351,10 @@ class EC_Group_Tests : public Test { result.confirm("point (0,0) is not on the curve", !zero_coords.on_the_curve()); } - static void test_point_swap(Test::Result& result, const Botan::EC_Group& group) { - Botan::EC_Point a(create_random_point(Test::rng(), group)); - Botan::EC_Point b(create_random_point(Test::rng(), group)); - b *= Botan::BigInt(Test::rng(), 20); + void test_point_swap(Test::Result& result, const Botan::EC_Group& group) { + Botan::EC_Point a(create_random_point(this->rng(), group)); + Botan::EC_Point b(create_random_point(this->rng(), group)); + b *= Botan::BigInt(this->rng(), 20); Botan::EC_Point c(a); Botan::EC_Point d(b); @@ -772,7 +774,7 @@ class ECC_Invalid_Key_Tests final : public Text_Based_Test { try { auto key = Botan::X509::load_key(key_data); - result.test_eq("public key fails check", key->check_key(Test::rng(), false), false); + result.test_eq("public key fails check", key->check_key(this->rng(), false), false); } catch(Botan::Decoding_Error&) { result.test_success("Decoding invalid ECC key results in decoding error exception"); } diff --git a/src/tests/test_ecc_pointmul.cpp b/src/tests/test_ecc_pointmul.cpp index 260c4fc9eee..360aabe8728 100644 --- a/src/tests/test_ecc_pointmul.cpp +++ b/src/tests/test_ecc_pointmul.cpp @@ -40,11 +40,11 @@ class ECC_Basepoint_Mul_Tests final : public Text_Based_Test { result.test_eq("p1 affine Y", p1.get_affine_y(), Y); std::vector ws; - const Botan::EC_Point p2 = group.blinded_base_point_multiply(m, Test::rng(), ws); + const Botan::EC_Point p2 = group.blinded_base_point_multiply(m, this->rng(), ws); result.test_eq("p2 affine X", p2.get_affine_x(), X); result.test_eq("p2 affine Y", p2.get_affine_y(), Y); - const Botan::EC_Point p3 = group.blinded_var_point_multiply(base_point, m, Test::rng(), ws); + const Botan::EC_Point p3 = group.blinded_var_point_multiply(base_point, m, this->rng(), ws); result.test_eq("p3 affine X", p3.get_affine_x(), X); result.test_eq("p3 affine Y", p3.get_affine_y(), Y); @@ -80,7 +80,7 @@ class ECC_Varpoint_Mul_Tests final : public Text_Based_Test { result.confirm("Output point is on the curve", p1.on_the_curve()); std::vector ws; - const Botan::EC_Point p2 = group.blinded_var_point_multiply(pt, k, Test::rng(), ws); + const Botan::EC_Point p2 = group.blinded_var_point_multiply(pt, k, this->rng(), ws); result.test_eq("p2 affine X", p2.get_affine_x(), kX); result.test_eq("p2 affine Y", p2.get_affine_y(), kY); diff --git a/src/tests/test_ecdh.cpp b/src/tests/test_ecdh.cpp index 95e87ee8ab5..e85a96f9b55 100644 --- a/src/tests/test_ecdh.cpp +++ b/src/tests/test_ecdh.cpp @@ -26,7 +26,7 @@ class ECDH_KAT_Tests final : public PK_Key_Agreement_Test { std::unique_ptr load_our_key(const std::string& group_id, const VarMap& vars) override { Botan::EC_Group group(group_id); const Botan::BigInt secret = vars.get_req_bn("Secret"); - return std::make_unique(Test::rng(), group, secret); + return std::make_unique(this->rng(), group, secret); } std::vector load_their_key(const std::string& /*header*/, const VarMap& vars) override { diff --git a/src/tests/test_ecdsa.cpp b/src/tests/test_ecdsa.cpp index 4951d353490..0cd7f0026cd 100644 --- a/src/tests/test_ecdsa.cpp +++ b/src/tests/test_ecdsa.cpp @@ -89,7 +89,7 @@ class ECDSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test { const BigInt x = vars.get_req_bn("X"); Botan::EC_Group group(Botan::OID::from_string(group_id)); - return std::make_unique(Test::rng(), group, x); + return std::make_unique(this->rng(), group, x); } std::string default_padding(const VarMap& vars) const override { return vars.get_req_str("Hash"); } @@ -98,7 +98,7 @@ class ECDSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test { std::unique_ptr test_rng(const std::vector& nonce) const override { // probabilistic ecdsa signature generation extracts more random than just the nonce, // but the nonce is extracted first - return std::make_unique(nonce, 1); + return std::make_unique(nonce, 1, this->rng()); } #endif }; @@ -124,7 +124,7 @@ class ECDSA_KAT_Verification_Tests final : public PK_Signature_Verification_Test const BigInt x = vars.get_req_bn("X"); Botan::EC_Group group(Botan::OID::from_string(group_id)); - Botan::ECDSA_PrivateKey priv_key(Test::rng(), group, x); + Botan::ECDSA_PrivateKey priv_key(this->rng(), group, x); return priv_key.public_key(); } @@ -136,8 +136,8 @@ class ECDSA_Sign_Verify_DER_Test final : public PK_Sign_Verify_DER_Test { public: ECDSA_Sign_Verify_DER_Test() : PK_Sign_Verify_DER_Test("ECDSA", "SHA-512") {} - std::unique_ptr key() const override { - return Botan::create_private_key("ECDSA", Test::rng(), "secp256r1"); + std::unique_ptr key() override { + return Botan::create_private_key("ECDSA", this->rng(), "secp256r1"); } }; @@ -227,7 +227,7 @@ class ECDSA_Invalid_Key_Tests final : public Text_Based_Test { } auto key = std::make_unique(group, *public_point); - result.test_eq("public key fails check", key->check_key(Test::rng(), false), false); + result.test_eq("public key fails check", key->check_key(this->rng(), false), false); return result; } }; diff --git a/src/tests/test_ecgdsa.cpp b/src/tests/test_ecgdsa.cpp index 18b759d1571..cc2d943fca7 100644 --- a/src/tests/test_ecgdsa.cpp +++ b/src/tests/test_ecgdsa.cpp @@ -31,7 +31,7 @@ class ECGDSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test { const BigInt x = vars.get_req_bn("X"); Botan::EC_Group group(Botan::OID::from_string(group_id)); - return std::make_unique(Test::rng(), group, x); + return std::make_unique(this->rng(), group, x); } std::string default_padding(const VarMap& vars) const override { return vars.get_req_str("Hash"); } @@ -39,7 +39,7 @@ class ECGDSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test { std::unique_ptr test_rng(const std::vector& nonce) const override { // ecgdsa signature generation extracts more random than just the nonce, // but the nonce is extracted first - return std::make_unique(nonce, 1); + return std::make_unique(nonce, 1, this->rng()); } }; diff --git a/src/tests/test_ecies.cpp b/src/tests/test_ecies.cpp index bae81a973cd..7a4040553d7 100644 --- a/src/tests/test_ecies.cpp +++ b/src/tests/test_ecies.cpp @@ -44,11 +44,12 @@ void check_encrypt_decrypt(Test::Result& result, const Botan::InitializationVector& iv, const std::string& label, const std::vector& plaintext, - const std::vector& ciphertext) { + const std::vector& ciphertext, + Botan::RandomNumberGenerator& rng) { try { - Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng()); + Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, rng); ecies_enc.set_other_key(other_private_key.public_point()); - Botan::ECIES_Decryptor ecies_dec(other_private_key, ecies_params, Test::rng()); + Botan::ECIES_Decryptor ecies_dec(other_private_key, ecies_params, rng); if(!iv.bits_of().empty()) { ecies_enc.set_initialization_vector(iv); ecies_dec.set_initialization_vector(iv); @@ -58,7 +59,7 @@ void check_encrypt_decrypt(Test::Result& result, ecies_dec.set_label(label); } - const std::vector encrypted = ecies_enc.encrypt(plaintext, Test::rng()); + const std::vector encrypted = ecies_enc.encrypt(plaintext, rng); if(!ciphertext.empty()) { result.test_eq("encrypted data", encrypted, ciphertext); } @@ -79,7 +80,8 @@ void check_encrypt_decrypt(Test::Result& result, const Botan::ECDH_PrivateKey& private_key, const Botan::ECDH_PrivateKey& other_private_key, const Botan::ECIES_System_Params& ecies_params, - size_t iv_length = 0) { + size_t iv_length, + Botan::RandomNumberGenerator& rng) { const std::vector plaintext{1, 2, 3}; check_encrypt_decrypt(result, private_key, @@ -88,7 +90,8 @@ void check_encrypt_decrypt(Test::Result& result, Botan::InitializationVector(std::vector(iv_length, 0)), "", plaintext, - std::vector()); + std::vector(), + rng); } #if defined(BOTAN_HAS_KDF1_18033) && defined(BOTAN_HAS_SHA1) @@ -119,12 +122,12 @@ class ECIES_ISO_Tests final : public Text_Based_Test { const Botan::EC_Group domain(p, a, b, gx, gy, mu, nu); // keys of bob - const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, x); + const Botan::ECDH_PrivateKey other_private_key(this->rng(), domain, x); const Botan::EC_Point other_public_key_point = domain.point(hx, hy); const Botan::ECDH_PublicKey other_public_key(domain, other_public_key_point); // (ephemeral) keys of alice - const Botan::ECDH_PrivateKey eph_private_key(Test::rng(), domain, r); + const Botan::ECDH_PrivateKey eph_private_key(this->rng(), domain, r); const Botan::EC_Point eph_public_key_point = eph_private_key.public_point(); const std::vector eph_public_key_bin = eph_public_key_point.encode(compression_type); result.test_eq("encoded (ephemeral) public key", eph_public_key_bin, c0); @@ -133,7 +136,7 @@ class ECIES_ISO_Tests final : public Text_Based_Test { // no cofactor-/oldcofactor-/singlehash-/check-mode and 128 byte secret length Botan::ECIES_KA_Params ka_params( eph_private_key.domain(), "KDF1-18033(SHA-1)", 128, compression_type, Flags::None); - const Botan::ECIES_KA_Operation ka(eph_private_key, ka_params, true, Test::rng()); + const Botan::ECIES_KA_Operation ka(eph_private_key, ka_params, true, this->rng()); const Botan::SymmetricKey secret_key = ka.derive_secret(eph_public_key_bin, other_public_key_point); result.test_eq("derived secret key", secret_key.bits_of(), k); @@ -171,7 +174,8 @@ class ECIES_ISO_Tests final : public Text_Based_Test { 20, comp_type, flags); - check_encrypt_decrypt(result, eph_private_key, other_private_key, ecies_params, 16); + check_encrypt_decrypt( + result, eph_private_key, other_private_key, ecies_params, 16, this->rng()); } } } @@ -217,12 +221,13 @@ class ECIES_Tests final : public Text_Based_Test { const Flags flags = ecies_flags(cofactor_mode, old_cofactor_mode, check_mode, single_hash_mode); const Botan::EC_Group domain(curve); - const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value); - const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, other_private_key_value); + const Botan::ECDH_PrivateKey private_key(this->rng(), domain, private_key_value); + const Botan::ECDH_PrivateKey other_private_key(this->rng(), domain, other_private_key_value); const Botan::ECIES_System_Params ecies_params( private_key.domain(), kdf, dem, dem_key_len, mac, mac_key_len, compression_type, flags); - check_encrypt_decrypt(result, private_key, other_private_key, ecies_params, iv, label, plaintext, ciphertext); + check_encrypt_decrypt( + result, private_key, other_private_key, ecies_params, iv, label, plaintext, ciphertext, this->rng()); return result; } @@ -235,6 +240,8 @@ BOTAN_REGISTER_TEST("pubkey", "ecies", ECIES_Tests); Test::Result test_other_key_not_set() { Test::Result result("ECIES other key not set"); + auto rng = Test::new_rng("ecies_other_key_not_set"); + const Flags flags = ecies_flags(false, false, false, true); const Botan::EC_Group domain("secp521r1"); const Botan::BigInt private_key_value( @@ -242,7 +249,7 @@ Test::Result test_other_key_not_set() { "5002761777100287880684822948852132235484464537021197213998300006" "547176718172344447619746779823"); - const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value); + const Botan::ECDH_PrivateKey private_key(*rng, domain, private_key_value); const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", @@ -252,10 +259,10 @@ Test::Result test_other_key_not_set() { Botan::EC_Point_Format::Compressed, flags); - Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng()); + Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, *rng); result.test_throws("encrypt not possible without setting other public key", - [&ecies_enc]() { ecies_enc.encrypt(std::vector(8), Test::rng()); }); + [&ecies_enc, &rng]() { ecies_enc.encrypt(std::vector(8), *rng); }); return result; } @@ -263,6 +270,8 @@ Test::Result test_other_key_not_set() { Test::Result test_kdf_not_found() { Test::Result result("ECIES kdf not found"); + auto rng = Test::new_rng("ecies_kdf_not_found"); + const Flags flags = ecies_flags(false, false, false, true); const Botan::EC_Group domain("secp521r1"); const Botan::BigInt private_key_value( @@ -270,7 +279,7 @@ Test::Result test_kdf_not_found() { "5002761777100287880684822948852132235484464537021197213998300006" "547176718172344447619746779823"); - const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value); + const Botan::ECDH_PrivateKey private_key(*rng, domain, private_key_value); const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF-XYZ(SHA-512)", "AES-256/CBC", @@ -281,8 +290,8 @@ Test::Result test_kdf_not_found() { flags); result.test_throws("kdf not found", [&]() { - Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng()); - ecies_enc.encrypt(std::vector(8), Test::rng()); + Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, *rng); + ecies_enc.encrypt(std::vector(8), *rng); }); return result; @@ -291,6 +300,8 @@ Test::Result test_kdf_not_found() { Test::Result test_mac_not_found() { Test::Result result("ECIES mac not found"); + auto rng = Test::new_rng("ecies_mac_not_found"); + const Flags flags = ecies_flags(false, false, false, true); const Botan::EC_Group domain("secp521r1"); const Botan::BigInt private_key_value( @@ -298,7 +309,7 @@ Test::Result test_mac_not_found() { "5002761777100287880684822948852132235484464537021197213998300006" "547176718172344447619746779823"); - const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value); + const Botan::ECDH_PrivateKey private_key(*rng, domain, private_key_value); const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", @@ -309,8 +320,8 @@ Test::Result test_mac_not_found() { flags); result.test_throws("mac not found", [&]() { - Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng()); - ecies_enc.encrypt(std::vector(8), Test::rng()); + Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, *rng); + ecies_enc.encrypt(std::vector(8), *rng); }); return result; @@ -319,6 +330,8 @@ Test::Result test_mac_not_found() { Test::Result test_cipher_not_found() { Test::Result result("ECIES cipher not found"); + auto rng = Test::new_rng("ecies_cipher_not_found"); + const Flags flags = ecies_flags(false, false, false, true); const Botan::EC_Group domain("secp521r1"); const Botan::BigInt private_key_value( @@ -326,7 +339,7 @@ Test::Result test_cipher_not_found() { "5002761777100287880684822948852132235484464537021197213998300006" "547176718172344447619746779823"); - const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value); + const Botan::ECDH_PrivateKey private_key(*rng, domain, private_key_value); const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-XYZ-256/CBC", @@ -337,8 +350,8 @@ Test::Result test_cipher_not_found() { flags); result.test_throws("cipher not found", [&]() { - Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng()); - ecies_enc.encrypt(std::vector(8), Test::rng()); + Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, *rng); + ecies_enc.encrypt(std::vector(8), *rng); }); return result; @@ -347,6 +360,8 @@ Test::Result test_cipher_not_found() { Test::Result test_system_params_short_ctor() { Test::Result result("ECIES short system params ctor"); + auto rng = Test::new_rng("ecies_params_short_ctor"); + const Botan::EC_Group domain("secp521r1"); const Botan::BigInt private_key_value( "405029866705438137604064977397053031159826489755682166267763407" @@ -358,8 +373,8 @@ Test::Result test_system_params_short_ctor() { "5866095315090327914760325168219669828915074071456176066304457448" "25404691681749451640151380153"); - const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value); - const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, other_private_key_value); + const Botan::ECDH_PrivateKey private_key(*rng, domain, private_key_value); + const Botan::ECDH_PrivateKey other_private_key(*rng, domain, other_private_key_value); const Botan::ECIES_System_Params ecies_params( private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", 32, "HMAC(SHA-512)", 16); @@ -380,7 +395,7 @@ Test::Result test_system_params_short_ctor() { "3F2463D233B22A7A12B679F4C06501F584D4DEFF6D26592A8D873398BD892" "B477B3468813C053DA43C4F3D49009F7A12D6EF7"); - check_encrypt_decrypt(result, private_key, other_private_key, ecies_params, iv, label, plaintext, ciphertext); + check_encrypt_decrypt(result, private_key, other_private_key, ecies_params, iv, label, plaintext, ciphertext, *rng); return result; } @@ -399,13 +414,15 @@ Test::Result test_ciphertext_too_short() { "5866095315090327914760325168219669828915074071456176066304457448" "25404691681749451640151380153"); - const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value); - const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, other_private_key_value); + auto rng = Test::new_rng("ecies_ciphertext_too_short"); + + const Botan::ECDH_PrivateKey private_key(*rng, domain, private_key_value); + const Botan::ECDH_PrivateKey other_private_key(*rng, domain, other_private_key_value); const Botan::ECIES_System_Params ecies_params( private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", 32, "HMAC(SHA-512)", 16); - Botan::ECIES_Decryptor ecies_dec(other_private_key, ecies_params, Test::rng()); + Botan::ECIES_Decryptor ecies_dec(other_private_key, ecies_params, *rng); result.test_throws("ciphertext too short", [&ecies_dec]() { ecies_dec.decrypt(Botan::hex_decode("0401519EAA0489FF9D51E98E4C22349A")); }); diff --git a/src/tests/test_eckcdsa.cpp b/src/tests/test_eckcdsa.cpp index fe047a9b150..e33b86594ee 100644 --- a/src/tests/test_eckcdsa.cpp +++ b/src/tests/test_eckcdsa.cpp @@ -29,7 +29,7 @@ class ECKCDSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test { const BigInt x = vars.get_req_bn("X"); Botan::EC_Group group(Botan::OID::from_string(group_id)); - return std::make_unique(Test::rng(), group, x); + return std::make_unique(this->rng(), group, x); } std::string default_padding(const VarMap& vars) const override { return vars.get_req_str("Hash"); } @@ -37,7 +37,7 @@ class ECKCDSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test { std::unique_ptr test_rng(const std::vector& nonce) const override { // eckcdsa signature generation extracts more random than just the nonce, // but the nonce is extracted first - return std::make_unique(nonce, 1); + return std::make_unique(nonce, 1, this->rng()); } }; diff --git a/src/tests/test_ed25519.cpp b/src/tests/test_ed25519.cpp index 6f7ed6b8b46..507ac6603cf 100644 --- a/src/tests/test_ed25519.cpp +++ b/src/tests/test_ed25519.cpp @@ -90,9 +90,9 @@ class Ed25519_Curdle_Format_Tests final : public Test { auto pub_key = Botan::X509::load_key(pub_data); result.confirm("Public key loaded", pub_key != nullptr); - Botan::PK_Signer signer(*priv_key, Test::rng(), "Pure"); + Botan::PK_Signer signer(*priv_key, this->rng(), "Pure"); signer.update("message"); - std::vector sig = signer.signature(Test::rng()); + std::vector sig = signer.signature(this->rng()); Botan::PK_Verifier verifier(*pub_key, "Pure"); verifier.update("message"); diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index a35c331af0f..c73075a60ce 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -814,6 +814,8 @@ class FFI_CBC_Cipher_Test final : public FFI_Test { TEST_FFI_OK(botan_cipher_output_length, (cipher_decrypt, ciphertext.size(), &ptext_len)); std::vector decrypted(ptext_len); + TEST_FFI_RC(0, botan_cipher_is_authenticated, (cipher_encrypt)); + TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, symkey.data(), symkey.size())); TEST_FFI_OK(botan_cipher_start, (cipher_decrypt, nonce.data(), nonce.size())); TEST_FFI_OK(botan_cipher_update, @@ -873,6 +875,8 @@ class FFI_GCM_Test final : public FFI_Test { TEST_FFI_OK(botan_cipher_get_tag_length, (cipher_encrypt, &tag_len)); result.test_int_eq(tag_len, 16, "Expected GCM tag length"); + TEST_FFI_RC(1, botan_cipher_is_authenticated, (cipher_encrypt)); + TEST_FFI_RC(1, botan_cipher_valid_nonce_length, (cipher_encrypt, 12)); // GCM accepts any nonce size except zero TEST_FFI_RC(0, botan_cipher_valid_nonce_length, (cipher_encrypt, 0)); @@ -988,6 +992,8 @@ class FFI_EAX_Test final : public FFI_Test { TEST_FFI_OK(botan_cipher_get_tag_length, (cipher_encrypt, &tag_len)); result.test_int_eq(tag_len, 16, "Expected EAX tag length"); + TEST_FFI_RC(1, botan_cipher_is_authenticated, (cipher_encrypt)); + TEST_FFI_RC(1, botan_cipher_valid_nonce_length, (cipher_encrypt, 12)); // EAX accepts any nonce size... TEST_FFI_RC(1, botan_cipher_valid_nonce_length, (cipher_encrypt, 0)); @@ -1079,6 +1085,8 @@ class FFI_StreamCipher_Test final : public FFI_Test { std::vector ct(pt.size()); + TEST_FFI_RC(0, botan_cipher_is_authenticated, (ctr)); + size_t input_consumed = 0; size_t output_written = 0; diff --git a/src/tests/test_frodokem.cpp b/src/tests/test_frodokem.cpp index 1323d0b5ed0..3a502467a06 100644 --- a/src/tests/test_frodokem.cpp +++ b/src/tests/test_frodokem.cpp @@ -61,7 +61,7 @@ class Frodo_KAT_Tests final : public Botan_Tests::PK_PQC_KEM_KAT_Test { }; std::vector test_frodo_roundtrips() { - auto& rng = Test::rng(); + auto rng = Test::new_rng("frodokem_roundtrip"); auto modes = std::vector{Botan::FrodoKEMMode::eFrodoKEM1344_SHAKE, Botan::FrodoKEMMode::eFrodoKEM976_SHAKE, @@ -97,34 +97,34 @@ std::vector test_frodo_roundtrips() { Botan::FrodoKEMConstants consts(mode); Test::Result& result = results.emplace_back("FrodoKEM roundtrip: " + m.to_string()); - Botan::FrodoKEM_PrivateKey sk1(rng, mode); + Botan::FrodoKEM_PrivateKey sk1(*rng, mode); Botan::FrodoKEM_PublicKey pk1(sk1.public_key_bits(), mode); // Happy case Botan::PK_KEM_Encryptor enc1(pk1, "Raw"); - const auto enc_res = enc1.encrypt(rng, 0 /* no KDF */); + const auto enc_res = enc1.encrypt(*rng, 0 /* no KDF */); result.test_eq("length of shared secret", enc_res.shared_key().size(), enc1.shared_key_length(0)); result.test_eq("length of ciphertext", enc_res.encapsulated_shared_key().size(), enc1.encapsulated_key_length()); - Botan::PK_KEM_Decryptor dec1(sk1, rng, "Raw"); + Botan::PK_KEM_Decryptor dec1(sk1, *rng, "Raw"); auto ss = dec1.decrypt(enc_res.encapsulated_shared_key(), 0 /* no KDF */); result.test_eq("shared secrets match", ss, enc_res.shared_key()); result.test_eq("length of shared secret (decaps)", ss.size(), dec1.shared_key_length(0)); // Decryption failures ("All right then, keep your secrets.") - Botan::FrodoKEM_PrivateKey sk2(rng, mode); + Botan::FrodoKEM_PrivateKey sk2(*rng, mode); // Decryption failure: mismatching private key - Botan::PK_KEM_Decryptor dec2(sk2, rng, "Raw"); + Botan::PK_KEM_Decryptor dec2(sk2, *rng, "Raw"); auto ss_mismatch = dec2.decrypt(enc_res.encapsulated_shared_key(), 0 /* no KDF */); result.test_eq("decryption failure sk", ss_mismatch, get_decryption_error_value(consts, enc_res.encapsulated_shared_key(), sk2)); // Decryption failure: bitflip in encapsulated shared value - const auto mutated_encaps_value = Test::mutate_vec(enc_res.encapsulated_shared_key()); + const auto mutated_encaps_value = Test::mutate_vec(enc_res.encapsulated_shared_key(), *rng); ss_mismatch = dec2.decrypt(mutated_encaps_value, 0 /* no KDF */); result.test_eq( "decryption failure bitflip", ss_mismatch, get_decryption_error_value(consts, mutated_encaps_value, sk2)); diff --git a/src/tests/test_gost_3410.cpp b/src/tests/test_gost_3410.cpp index 2c2a873aee1..3e5c49bb17d 100644 --- a/src/tests/test_gost_3410.cpp +++ b/src/tests/test_gost_3410.cpp @@ -68,13 +68,13 @@ class GOST_3410_2001_Signature_Tests final : public PK_Signature_Generation_Test const BigInt x = vars.get_req_bn("X"); - return std::make_unique(Test::rng(), group, x); + return std::make_unique(this->rng(), group, x); } std::string default_padding(const VarMap& vars) const override { return vars.get_req_str("Hash"); } std::unique_ptr test_rng(const std::vector& nonce) const override { - return std::make_unique(nonce, 1); + return std::make_unique(nonce, 1, this->rng()); } }; diff --git a/src/tests/test_hash.cpp b/src/tests/test_hash.cpp index 63dfe8ef623..2bcf731fe37 100644 --- a/src/tests/test_hash.cpp +++ b/src/tests/test_hash.cpp @@ -131,7 +131,7 @@ class Hash_Function_Tests final : public Text_Based_Test { size_t so_far = 1; while(so_far < input.size()) { - size_t take = Test::rng().next_byte() % (input.size() - so_far); + size_t take = this->rng().next_byte() % (input.size() - so_far); if(input.size() - so_far == 1) { take = 1; diff --git a/src/tests/test_kyber.cpp b/src/tests/test_kyber.cpp index a451b3d504d..58f116afd29 100644 --- a/src/tests/test_kyber.cpp +++ b/src/tests/test_kyber.cpp @@ -38,10 +38,12 @@ class KYBER_Tests final : public Test { static Test::Result run_kyber_test(const char* test_name, Botan::KyberMode mode, size_t strength) { Test::Result result(test_name); + auto rng = Test::new_rng(test_name); + const std::vector empty_salt; // Alice - const Botan::Kyber_PrivateKey priv_key(Test::rng(), mode); + const Botan::Kyber_PrivateKey priv_key(*rng, mode); const auto pub_key = priv_key.public_key(); result.test_eq("estimated strength private", priv_key.estimated_strength(), strength); @@ -54,11 +56,11 @@ class KYBER_Tests final : public Test { // Bob (reading from serialized public key) Botan::Kyber_PublicKey alice_pub_key(pub_key_bits, mode); auto enc = Botan::PK_KEM_Encryptor(alice_pub_key, "Raw", "base"); - const auto kem_result = enc.encrypt(Test::rng()); + const auto kem_result = enc.encrypt(*rng); // Alice (reading from serialized private key) Botan::Kyber_PrivateKey alice_priv_key(priv_key_bits, mode); - auto dec = Botan::PK_KEM_Decryptor(alice_priv_key, Test::rng(), "Raw", "base"); + auto dec = Botan::PK_KEM_Decryptor(alice_priv_key, *rng, "Raw", "base"); const auto key_alice = dec.decrypt(kem_result.encapsulated_shared_key(), 0 /* no KDF */, empty_salt); result.test_eq("shared secrets are equal", key_alice, kem_result.shared_key()); diff --git a/src/tests/test_mac.cpp b/src/tests/test_mac.cpp index 47a53a65215..3ae2d8065be 100644 --- a/src/tests/test_mac.cpp +++ b/src/tests/test_mac.cpp @@ -106,7 +106,7 @@ class Message_Auth_Tests final : public Text_Based_Test { result.test_eq("Clone has same name", mac->name(), clone->name()); clone->set_key(key); clone->start(iv); - clone->update(Test::rng().random_vec(32)); + clone->update(this->rng().random_vec(32)); result.test_eq(provider + " verify mac", mac->verify_mac(expected.data(), expected.size()), true); diff --git a/src/tests/test_mceliece.cpp b/src/tests/test_mceliece.cpp index 43dbd4aaa94..170958ec6e9 100644 --- a/src/tests/test_mceliece.cpp +++ b/src/tests/test_mceliece.cpp @@ -67,7 +67,7 @@ class McEliece_Keygen_Encrypt_Test final : public Text_Based_Test { try { Botan::PK_KEM_Encryptor kem_enc(mce_priv, "KDF1(SHA-512)"); - Botan::PK_KEM_Decryptor kem_dec(mce_priv, Test::rng(), "KDF1(SHA-512)"); + Botan::PK_KEM_Decryptor kem_dec(mce_priv, this->rng(), "KDF1(SHA-512)"); const auto kem_result = kem_enc.encrypt(rng, 64); @@ -144,7 +144,7 @@ class McEliece_Tests final : public Test { Test::Result result("McEliece keygen"); result.start_timer(); - Botan::McEliece_PrivateKey sk1(Test::rng(), param_sets[i].code_length, t); + Botan::McEliece_PrivateKey sk1(this->rng(), param_sets[i].code_length, t); const Botan::McEliece_PublicKey& pk1 = sk1; const std::vector pk_enc = pk1.public_key_bits(); @@ -155,7 +155,7 @@ class McEliece_Tests final : public Test { result.test_eq("decoded public key equals original", fingerprint(pk1), fingerprint(pk)); result.test_eq("decoded private key equals original", fingerprint(sk1), fingerprint(sk)); - result.test_eq("key validation passes", sk.check_key(Test::rng(), false), true); + result.test_eq("key validation passes", sk.check_key(this->rng(), false), true); result.end_timer(); result.end_timer(); @@ -163,7 +163,7 @@ class McEliece_Tests final : public Test { results.push_back(result); #if defined(BOTAN_HAS_KDF2) - results.push_back(test_kem(sk, pk)); + results.push_back(test_kem(sk, pk, this->rng())); #endif } } @@ -172,18 +172,20 @@ class McEliece_Tests final : public Test { } private: - static Test::Result test_kem(const Botan::McEliece_PrivateKey& sk, const Botan::McEliece_PublicKey& pk) { + static Test::Result test_kem(const Botan::McEliece_PrivateKey& sk, + const Botan::McEliece_PublicKey& pk, + Botan::RandomNumberGenerator& rng) { Test::Result result("McEliece KEM"); result.start_timer(); Botan::PK_KEM_Encryptor enc_op(pk, "KDF2(SHA-256)"); - Botan::PK_KEM_Decryptor dec_op(sk, Test::rng(), "KDF2(SHA-256)"); + Botan::PK_KEM_Decryptor dec_op(sk, rng, "KDF2(SHA-256)"); const size_t trials = (Test::run_long_tests() ? 30 : 10); for(size_t i = 0; i < trials; i++) { - Botan::secure_vector salt = Test::rng().random_vec(i); + Botan::secure_vector salt = rng.random_vec(i); - const auto kem_result = enc_op.encrypt(Test::rng(), 64, salt); + const auto kem_result = enc_op.encrypt(rng, 64, salt); Botan::secure_vector shared_key2 = dec_op.decrypt(kem_result.encapsulated_shared_key(), 64, salt); diff --git a/src/tests/test_modes.cpp b/src/tests/test_modes.cpp index 701ecec3836..c61325af5e4 100644 --- a/src/tests/test_modes.cpp +++ b/src/tests/test_modes.cpp @@ -69,13 +69,13 @@ class Cipher_Mode_Tests final : public Text_Based_Test { enc->ideal_granularity() % enc->update_granularity() == 0); try { - test_mode(result, algo, provider_ask, "encryption", *enc, key, nonce, input, expected); + test_mode(result, algo, provider_ask, "encryption", *enc, key, nonce, input, expected, this->rng()); } catch(Botan::Exception& e) { result.test_failure("Encryption tests failed", e.what()); } try { - test_mode(result, algo, provider_ask, "decryption", *dec, key, nonce, expected, input); + test_mode(result, algo, provider_ask, "decryption", *dec, key, nonce, expected, input, this->rng()); } catch(Botan::Exception& e) { result.test_failure("Decryption tests failed", e.what()); } @@ -93,7 +93,8 @@ class Cipher_Mode_Tests final : public Text_Based_Test { const std::vector& key, const std::vector& nonce, const std::vector& input, - const std::vector& expected) { + const std::vector& expected, + Botan::RandomNumberGenerator& rng) { const bool is_cbc = (algo.find("/CBC") != std::string::npos); const bool is_ctr = (algo.find("CTR") != std::string::npos); @@ -139,19 +140,19 @@ class Cipher_Mode_Tests final : public Text_Based_Test { result.test_eq("Large nonce not allowed", mode.valid_nonce_length(large_nonce_size), false); result.test_throws("Large nonce causes exception", [&mode]() { mode.start(nullptr, large_nonce_size); }); - Botan::secure_vector garbage = Test::rng().random_vec(update_granularity); + Botan::secure_vector garbage = rng.random_vec(update_granularity); // Test to make sure reset() resets what we need it to result.test_throws("Cannot process data (update) until key is set", [&]() { mode.update(garbage); }); result.test_throws("Cannot process data (finish) until key is set", [&]() { mode.finish(garbage); }); - mode.set_key(mutate_vec(key)); + mode.set_key(mutate_vec(key, rng)); if(is_ctr == false) { result.test_throws("Cannot process data until nonce is set", [&]() { mode.update(garbage); }); } - mode.start(mutate_vec(nonce)); + mode.start(mutate_vec(nonce, rng)); mode.reset(); if(is_ctr == false) { @@ -159,7 +160,7 @@ class Cipher_Mode_Tests final : public Text_Based_Test { [&]() { mode.update(garbage); }); } - mode.start(mutate_vec(nonce)); + mode.start(mutate_vec(nonce, rng)); mode.update(garbage); mode.reset(); diff --git a/src/tests/test_octetstring.cpp b/src/tests/test_octetstring.cpp index de15e384287..9462faa8b3b 100644 --- a/src/tests/test_octetstring.cpp +++ b/src/tests/test_octetstring.cpp @@ -17,7 +17,9 @@ namespace { Test::Result test_from_rng() { Test::Result result("OctetString"); - Botan::OctetString os(Test::rng(), 32); + auto rng = Test::new_rng("octet_string_from_rng"); + + Botan::OctetString os(*rng, 32); result.test_eq("length is 32 bytes", os.size(), 32); return result; @@ -35,7 +37,8 @@ Test::Result test_from_hex() { Test::Result test_from_byte() { Test::Result result("OctetString"); - auto rand_bytes = Test::rng().random_vec(8); + auto rng = Test::new_rng("octet_string_from_byte"); + auto rand_bytes = rng->random_vec(8); Botan::OctetString os(rand_bytes.data(), rand_bytes.size()); result.test_eq("length is 8 bytes", os.size(), 8); diff --git a/src/tests/test_passhash.cpp b/src/tests/test_passhash.cpp index 6425562e01f..a4b10488b82 100644 --- a/src/tests/test_passhash.cpp +++ b/src/tests/test_passhash.cpp @@ -40,7 +40,7 @@ class Bcrypt_Tests final : public Text_Based_Test { // self-test low levels for each test password for(uint16_t level = 4; level <= 6; ++level) { - const std::string gen_hash = Botan::generate_bcrypt(password, Test::rng(), level); + const std::string gen_hash = Botan::generate_bcrypt(password, this->rng(), level); result.test_eq("generated hash accepted", Botan::check_bcrypt(password, gen_hash), true); } @@ -56,13 +56,15 @@ class Bcrypt_Tests final : public Text_Based_Test { const uint16_t max_level = (Test::run_long_tests() ? 15 : 10); + auto& rng = this->rng(); + for(uint16_t level = 4; level <= max_level; ++level) { - const std::string gen_hash = Botan::generate_bcrypt(password, Test::rng(), level); + const std::string gen_hash = Botan::generate_bcrypt(password, rng, level); result.test_eq("generated hash accepted", Botan::check_bcrypt(password, gen_hash), true); } - result.test_throws("Invalid bcrypt version rejected", "Unknown bcrypt version 'q'", []() { - Botan::generate_bcrypt("pass", Test::rng(), 4, 'q'); + result.test_throws("Invalid bcrypt version rejected", "Unknown bcrypt version 'q'", [&rng]() { + Botan::generate_bcrypt("pass", rng, 4, 'q'); }); result.set_ns_consumed(Test::timestamp() - start); @@ -138,7 +140,7 @@ class Passhash9_Tests final : public Text_Based_Test { for(uint8_t alg_id = 0; alg_id <= 4; ++alg_id) { if(Botan::is_passhash9_alg_supported(alg_id)) { - const std::string gen_hash = Botan::generate_passhash9(password, Test::rng(), 2, alg_id); + const std::string gen_hash = Botan::generate_passhash9(password, this->rng(), 2, alg_id); if(!result.test_eq("generated hash accepted", Botan::check_passhash9(password, gen_hash), true)) { result.test_note("hash was " + gen_hash); @@ -151,7 +153,7 @@ class Passhash9_Tests final : public Text_Based_Test { for(uint16_t level = 1; level <= max_level; ++level) { const uint8_t alg_id = 1; // default used by generate_passhash9() if(Botan::is_passhash9_alg_supported(alg_id)) { - const std::string gen_hash = Botan::generate_passhash9(password, Test::rng(), level, alg_id); + const std::string gen_hash = Botan::generate_passhash9(password, this->rng(), level, alg_id); if(!result.test_eq("generated hash accepted", Botan::check_passhash9(password, gen_hash), true)) { result.test_note("hash was " + gen_hash); } @@ -166,9 +168,11 @@ class Passhash9_Tests final : public Text_Based_Test { result.confirm("Unknown algorithm is unknown", Botan::is_passhash9_alg_supported(255) == false); - result.test_throws("Throws if algorithm not supported", "Passhash9: Algorithm id 255 is not defined", []() { - Botan::generate_passhash9("pass", Test::rng(), 3, 255); - }); + auto& rng = this->rng(); + + result.test_throws("Throws if algorithm not supported", + "Passhash9: Algorithm id 255 is not defined", + [&rng]() { Botan::generate_passhash9("pass", rng, 3, 255); }); result.test_throws( "Throws if iterations is too high", "Requested passhash9 work factor 513 is too large", []() { diff --git a/src/tests/test_pkcs11_high_level.cpp b/src/tests/test_pkcs11_high_level.cpp index 759439c3cd7..d70fddf1180 100644 --- a/src/tests/test_pkcs11_high_level.cpp +++ b/src/tests/test_pkcs11_high_level.cpp @@ -610,9 +610,11 @@ Test::Result test_rsa_privkey_import() { TestSession test_session(true); + auto rng = Test::new_rng(__func__); + // create private key - RSA_PrivateKey priv_key(Test::rng(), 2048); - result.confirm("Key self test OK", priv_key.check_key(Test::rng(), true)); + RSA_PrivateKey priv_key(*rng, 2048); + result.confirm("Key self test OK", priv_key.check_key(*rng, true)); // import to card RSA_PrivateKeyImportProperties props(priv_key.get_n(), priv_key.get_d()); @@ -630,7 +632,7 @@ Test::Result test_rsa_privkey_import() { PKCS11_RSA_PrivateKey pk(test_session.session(), props); result.test_success("RSA private key import was successful"); - result.confirm("PK self test OK", pk.check_key(Test::rng(), true)); + result.confirm("PK self test OK", pk.check_key(*rng, true)); pk.destroy(); return result; @@ -641,8 +643,10 @@ Test::Result test_rsa_privkey_export() { TestSession test_session(true); + auto rng = Test::new_rng(__func__); + // create private key - RSA_PrivateKey priv_key(Test::rng(), 2048); + RSA_PrivateKey priv_key(*rng, 2048); // import to card RSA_PrivateKeyImportProperties props(priv_key.get_n(), priv_key.get_d()); @@ -661,11 +665,11 @@ Test::Result test_rsa_privkey_export() { props.set_sensitive(false); PKCS11_RSA_PrivateKey pk(test_session.session(), props); - result.confirm("Check PK11 key", pk.check_key(Test::rng(), true)); + result.confirm("Check PK11 key", pk.check_key(*rng, true)); RSA_PrivateKey exported = pk.export_key(); result.test_success("RSA private key export was successful"); - result.confirm("Check exported key", exported.check_key(Test::rng(), true)); + result.confirm("Check exported key", exported.check_key(*rng, true)); pk.destroy(); return result; @@ -676,8 +680,10 @@ Test::Result test_rsa_pubkey_import() { TestSession test_session(true); + auto rng = Test::new_rng(__func__); + // create public key from private key - RSA_PrivateKey priv_key(Test::rng(), 2048); + RSA_PrivateKey priv_key(*rng, 2048); // import to card RSA_PublicKeyImportProperties props(priv_key.get_n(), priv_key.get_e()); @@ -687,7 +693,7 @@ Test::Result test_rsa_pubkey_import() { PKCS11_RSA_PublicKey pk(test_session.session(), props); result.test_success("RSA public key import was successful"); - result.confirm("Check PK11 key", pk.check_key(Test::rng(), true)); + result.confirm("Check PK11 key", pk.check_key(*rng, true)); pk.destroy(); @@ -751,13 +757,15 @@ Test::Result test_rsa_encrypt_decrypt() { // generate key pair PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); + auto rng = Test::new_rng(__func__); + auto encrypt_and_decrypt = - [&keypair, &result](const std::vector& plaintext, const std::string& padding, const bool blinding) { + [&](const std::vector& plaintext, const std::string& padding, const bool blinding) { std::vector encrypted; try { - Botan::PK_Encryptor_EME encryptor(keypair.first, Test::rng(), padding); - encrypted = encryptor.encrypt(plaintext, Test::rng()); + Botan::PK_Encryptor_EME encryptor(keypair.first, *rng, padding); + encrypted = encryptor.encrypt(plaintext, *rng); } catch(Botan::PKCS11::PKCS11_ReturnError& e) { result.test_failure("PKCS11 RSA encrypt " + padding, e.what()); } @@ -766,7 +774,7 @@ Test::Result test_rsa_encrypt_decrypt() { try { keypair.second.set_use_software_padding(blinding); - Botan::PK_Decryptor_EME decryptor(keypair.second, Test::rng(), padding); + Botan::PK_Decryptor_EME decryptor(keypair.second, *rng, padding); decrypted = decryptor.decrypt(encrypted); } catch(Botan::PKCS11::PKCS11_ReturnError& e) { std::ostringstream err; @@ -805,17 +813,19 @@ Test::Result test_rsa_sign_verify() { // generate key pair PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); + auto rng = Test::new_rng(__func__); + std::vector plaintext(256); std::iota(std::begin(plaintext), std::end(plaintext), static_cast(0)); - auto sign_and_verify = [&keypair, &plaintext, &result](const std::string& emsa, bool multipart) { - Botan::PK_Signer signer(keypair.second, Test::rng(), emsa, Botan::Signature_Format::Standard); + auto sign_and_verify = [&](const std::string& emsa, bool multipart) { + Botan::PK_Signer signer(keypair.second, *rng, emsa, Botan::Signature_Format::Standard); std::vector signature; if(multipart) { signer.update(plaintext.data(), plaintext.size() / 2); - signature = signer.sign_message(plaintext.data() + plaintext.size() / 2, plaintext.size() / 2, Test::rng()); + signature = signer.sign_message(plaintext.data() + plaintext.size() / 2, plaintext.size() / 2, *rng); } else { - signature = signer.sign_message(plaintext, Test::rng()); + signature = signer.sign_message(plaintext, *rng); } Botan::PK_Verifier verifier(keypair.first, emsa, Botan::Signature_Format::Standard); @@ -882,9 +892,11 @@ Test::Result test_ecdsa_privkey_import() { TestSession test_session(true); + auto rng = Test::new_rng(__func__); + // create ecdsa private key - ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); - result.confirm("Key self test OK", priv_key.check_key(Test::rng(), true)); + ECDSA_PrivateKey priv_key(*rng, EC_Group("secp256r1")); + result.confirm("Key self test OK", priv_key.check_key(*rng, true)); // import to card EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); @@ -899,7 +911,7 @@ Test::Result test_ecdsa_privkey_import() { PKCS11_ECDSA_PrivateKey pk(test_session.session(), props); result.test_success("ECDSA private key import was successful"); pk.set_public_point(priv_key.public_point()); - result.confirm("P11 key self test OK", pk.check_key(Test::rng(), false)); + result.confirm("P11 key self test OK", pk.check_key(*rng, false)); pk.destroy(); return result; @@ -910,10 +922,12 @@ Test::Result test_ecdsa_privkey_export() { TestSession test_session(true); + auto rng = Test::new_rng(__func__); + // create private key - ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + ECDSA_PrivateKey priv_key(*rng, EC_Group("secp256r1")); - result.confirm("Check ECDSA key", priv_key.check_key(Test::rng(), true)); + result.confirm("Check ECDSA key", priv_key.check_key(*rng, true)); // import to card EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); props.set_token(true); @@ -927,11 +941,11 @@ Test::Result test_ecdsa_privkey_export() { PKCS11_ECDSA_PrivateKey pk(test_session.session(), props); pk.set_public_point(priv_key.public_point()); - result.confirm("Check PK11 key", pk.check_key(Test::rng(), false)); + result.confirm("Check PK11 key", pk.check_key(*rng, false)); ECDSA_PrivateKey exported = pk.export_key(); result.test_success("ECDSA private key export was successful"); - result.confirm("Check exported key valid", exported.check_key(Test::rng(), true)); + result.confirm("Check exported key valid", exported.check_key(*rng, true)); result.test_eq("Check exported key contents", exported.private_key_bits(), priv_key.private_key_bits()); pk.destroy(); @@ -943,8 +957,10 @@ Test::Result test_ecdsa_pubkey_import() { TestSession test_session(true); + auto rng = Test::new_rng(__func__); + // create ecdsa private key - ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + ECDSA_PrivateKey priv_key(*rng, EC_Group("secp256r1")); const auto enc_point = encode_ec_point_in_octet_str(priv_key.public_point()); @@ -970,8 +986,10 @@ Test::Result test_ecdsa_pubkey_export() { TestSession test_session(true); + auto rng = Test::new_rng(__func__); + // create public key from private key - ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + ECDSA_PrivateKey priv_key(*rng, EC_Group("secp256r1")); const auto enc_point = encode_ec_point_in_octet_str(priv_key.public_point()); @@ -1066,16 +1084,17 @@ Test::Result test_ecdsa_sign_verify_core(EC_Group_Encoding ec_dompar_enc, const SlotInfo info = slot.get_slot_info(); std::string manufacturer(reinterpret_cast(info.manufacturerID)); + auto rng = Test::new_rng(__func__); + for(auto& curve : curves) { // generate key pair PKCS11_ECDSA_KeyPair keypair = generate_ecdsa_keypair(test_session, curve, ec_dompar_enc); std::vector plaintext(20, 0x01); - auto sign_and_verify = [&keypair, &plaintext, &result]( - const std::string& emsa, const Botan::Signature_Format format, bool check_soft) { - Botan::PK_Signer signer(keypair.second, Test::rng(), emsa, format); - auto signature = signer.sign_message(plaintext, Test::rng()); + auto sign_and_verify = [&](const std::string& emsa, const Botan::Signature_Format format, bool check_soft) { + Botan::PK_Signer signer(keypair.second, *rng, emsa, format); + auto signature = signer.sign_message(plaintext, *rng); Botan::PK_Verifier token_verifier(keypair.first, emsa, format); bool ecdsa_ok = token_verifier.verify_message(plaintext, signature); @@ -1150,8 +1169,10 @@ Test::Result test_ecdh_privkey_import() { TestSession test_session(true); + auto rng = Test::new_rng(__func__); + // create ecdh private key - ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + ECDH_PrivateKey priv_key(*rng, EC_Group("secp256r1")); // import to card EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); @@ -1175,8 +1196,10 @@ Test::Result test_ecdh_privkey_export() { TestSession test_session(true); + auto rng = Test::new_rng(__func__); + // create private key - ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + ECDH_PrivateKey priv_key(*rng, EC_Group("secp256r1")); // import to card EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); @@ -1203,8 +1226,10 @@ Test::Result test_ecdh_pubkey_import() { TestSession test_session(true); + auto rng = Test::new_rng(__func__); + // create ECDH private key - ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + ECDH_PrivateKey priv_key(*rng, EC_Group("secp256r1")); const auto enc_point = encode_ec_point_in_octet_str(priv_key.public_point()); @@ -1230,8 +1255,10 @@ Test::Result test_ecdh_pubkey_export() { TestSession test_session(true); + auto rng = Test::new_rng(__func__); + // create public key from private key - ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + ECDH_PrivateKey priv_key(*rng, EC_Group("secp256r1")); const auto enc_point = encode_ec_point_in_octet_str(priv_key.public_point()); @@ -1313,9 +1340,11 @@ Test::Result test_ecdh_derive() { PKCS11_ECDH_KeyPair keypair = generate_ecdh_keypair(test_session, "Botan test ECDH key1"); PKCS11_ECDH_KeyPair keypair2 = generate_ecdh_keypair(test_session, "Botan test ECDH key2"); + auto rng = Test::new_rng(__func__); + // SoftHSMv2 only supports CKD_NULL KDF at the moment - Botan::PK_Key_Agreement ka(keypair.second, Test::rng(), "Raw"); - Botan::PK_Key_Agreement kb(keypair2.second, Test::rng(), "Raw"); + Botan::PK_Key_Agreement ka(keypair.second, *rng, "Raw"); + Botan::PK_Key_Agreement kb(keypair2.second, *rng, "Raw"); Botan::SymmetricKey alice_key = ka.derive_key(32, keypair2.first.public_point().encode(EC_Point_Format::Uncompressed)); @@ -1358,11 +1387,11 @@ Test::Result test_rng_generate_random() { Test::Result result("PKCS11 RNG generate random"); TestSession test_session(true); - PKCS11_RNG rng(test_session.session()); - result.confirm("RNG already seeded", rng.is_seeded()); + PKCS11_RNG p11_rng(test_session.session()); + result.confirm("RNG already seeded", p11_rng.is_seeded()); std::vector random(20); - rng.randomize(random.data(), random.size()); + p11_rng.randomize(random.data(), random.size()); result.test_ne("random data generated", random, std::vector(20)); return result; @@ -1372,18 +1401,19 @@ Test::Result test_rng_add_entropy() { Test::Result result("PKCS11 RNG add entropy random"); TestSession test_session(true); - PKCS11_RNG rng(test_session.session()); + PKCS11_RNG p11_rng(test_session.session()); - result.confirm("RNG already seeded", rng.is_seeded()); - rng.clear(); - result.confirm("RNG ignores call to clear", rng.is_seeded()); + result.confirm("RNG already seeded", p11_rng.is_seeded()); + p11_rng.clear(); + result.confirm("RNG ignores call to clear", p11_rng.is_seeded()); result.test_eq("RNG ignores calls to reseed", - rng.reseed(Botan::Entropy_Sources::global_sources(), 256, std::chrono::milliseconds(300)), + p11_rng.reseed(Botan::Entropy_Sources::global_sources(), 256, std::chrono::milliseconds(300)), 0); - auto random = Test::rng().random_vec(20); - rng.add_entropy(random.data(), random.size()); + auto rng = Test::new_rng(__func__); + auto random = rng->random_vec(20); + p11_rng.add_entropy(random.data(), random.size()); result.test_success("entropy added"); return result; diff --git a/src/tests/test_psk_db.cpp b/src/tests/test_psk_db.cpp index 36ccbe43d0f..f97a0abb401 100644 --- a/src/tests/test_psk_db.cpp +++ b/src/tests/test_psk_db.cpp @@ -159,7 +159,7 @@ class PSK_DB_Tests final : public Test { Test::Result result("PSK_DB SQL"); const Botan::secure_vector zeros(32); - const Botan::secure_vector not_zeros = Test::rng().random_vec(32); + const Botan::secure_vector not_zeros = this->rng().random_vec(32); const std::string table_name = "bobby"; std::shared_ptr sqldb = std::make_shared(":memory:"); diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp index 0f7ec9b8ee3..0974a8eff45 100644 --- a/src/tests/test_pubkey.cpp +++ b/src/tests/test_pubkey.cpp @@ -27,17 +27,20 @@ namespace Botan_Tests { +namespace { + void check_invalid_signatures(Test::Result& result, Botan::PK_Verifier& verifier, const std::vector& message, - const std::vector& signature) { + const std::vector& signature, + Botan::RandomNumberGenerator& rng) { const size_t tests_to_run = (Test::run_long_tests() ? 20 : 5); const std::vector zero_sig(signature.size()); result.test_eq("all zero signature invalid", verifier.verify_message(message, zero_sig), false); for(size_t i = 0; i < tests_to_run; ++i) { - const std::vector bad_sig = Test::mutate_vec(signature); + const std::vector bad_sig = Test::mutate_vec(signature, rng); try { if(!result.test_eq("incorrect signature invalid", verifier.verify_message(message, bad_sig), false)) { @@ -50,16 +53,20 @@ void check_invalid_signatures(Test::Result& result, } } +} // namespace + +// Exposed for DLIES tests void check_invalid_ciphertexts(Test::Result& result, Botan::PK_Decryptor& decryptor, const std::vector& plaintext, - const std::vector& ciphertext) { + const std::vector& ciphertext, + Botan::RandomNumberGenerator& rng) { const size_t tests_to_run = (Test::run_long_tests() ? 20 : 5); size_t ciphertext_accepted = 0, ciphertext_rejected = 0; for(size_t i = 0; i < tests_to_run; ++i) { - const std::vector bad_ctext = Test::mutate_vec(ciphertext); + const std::vector bad_ctext = Test::mutate_vec(ciphertext, rng); try { const Botan::secure_vector decrypted = decryptor.decrypt(bad_ctext); @@ -133,7 +140,7 @@ Test::Result PK_Signature_Generation_Test::run_one_test(const std::string& pad_h result.test_eq("KAT signature valid", verifier->verify_message(message, signature), true); - check_invalid_signatures(result, *verifier, message, signature); + check_invalid_signatures(result, *verifier, message, signature, this->rng()); result.test_eq("KAT signature valid (try 2)", verifier->verify_message(message, signature), true); @@ -147,13 +154,13 @@ Test::Result PK_Signature_Generation_Test::run_one_test(const std::string& pad_h try { signer = std::make_unique( - *privkey, Test::rng(), padding, Botan::Signature_Format::Standard, sign_provider); + *privkey, this->rng(), padding, Botan::Signature_Format::Standard, sign_provider); if(vars.has_key("Nonce")) { auto rng = test_rng(vars.get_req_bin("Nonce")); generated_signature = signer->sign_message(message, *rng); } else { - generated_signature = signer->sign_message(message, Test::rng()); + generated_signature = signer->sign_message(message, this->rng()); } result.test_lte( @@ -222,7 +229,7 @@ Test::Result PK_Signature_Verification_Test::run_one_test(const std::string& pad result.test_eq("correct signature valid with " + verify_provider, verified, true); if(test_random_invalid_sigs()) { - check_invalid_signatures(result, *verifier, message, signature); + check_invalid_signatures(result, *verifier, message, signature, this->rng()); } } else { result.confirm("incorrect signature is rejected", verified == false); @@ -274,7 +281,7 @@ std::vector PK_Sign_Verify_DER_Test::run() { try { signer = std::make_unique( - *privkey, Test::rng(), padding, Botan::Signature_Format::DerSequence, provider); + *privkey, this->rng(), padding, Botan::Signature_Format::DerSequence, provider); verifier = std::make_unique(*privkey, padding, Botan::Signature_Format::DerSequence, provider); } catch(Botan::Lookup_Error& e) { @@ -283,13 +290,13 @@ std::vector PK_Sign_Verify_DER_Test::run() { if(signer && verifier) { try { - std::vector generated_signature = signer->sign_message(message, Test::rng()); + std::vector generated_signature = signer->sign_message(message, this->rng()); const bool verified = verifier->verify_message(message, generated_signature); result.test_eq("correct signature valid with " + provider, verified, true); if(test_random_invalid_sigs()) { - check_invalid_signatures(result, *verifier, message, generated_signature); + check_invalid_signatures(result, *verifier, message, generated_signature, this->rng()); } } catch(std::exception& e) { result.test_failure("verification threw exception", e.what()); @@ -328,7 +335,7 @@ Test::Result PK_Encryption_Decryption_Test::run_one_test(const std::string& pad_ std::unique_ptr decryptor; try { - decryptor = std::make_unique(*privkey, Test::rng(), padding, dec_provider); + decryptor = std::make_unique(*privkey, this->rng(), padding, dec_provider); } catch(Botan::Lookup_Error&) { continue; } @@ -343,14 +350,14 @@ Test::Result PK_Encryption_Decryption_Test::run_one_test(const std::string& pad_ } result.test_eq(dec_provider, "decryption of KAT", decrypted, plaintext); - check_invalid_ciphertexts(result, *decryptor, plaintext, ciphertext); + check_invalid_ciphertexts(result, *decryptor, plaintext, ciphertext, this->rng()); } for(const auto& enc_provider : possible_providers(algo_name())) { std::unique_ptr encryptor; try { - encryptor = std::make_unique(*pubkey, Test::rng(), padding, enc_provider); + encryptor = std::make_unique(*pubkey, this->rng(), padding, enc_provider); } catch(Botan::Lookup_Error&) { continue; } @@ -370,7 +377,7 @@ Test::Result PK_Encryption_Decryption_Test::run_one_test(const std::string& pad_ result.test_lte("Input within accepted bounds", plaintext.size(), encryptor->maximum_input_size()); } - const std::vector generated_ciphertext = encryptor->encrypt(plaintext, kat_rng ? *kat_rng : Test::rng()); + const std::vector generated_ciphertext = encryptor->encrypt(plaintext, kat_rng ? *kat_rng : this->rng()); result.test_lte( "Ciphertext within length", generated_ciphertext.size(), encryptor->ciphertext_length(plaintext.size())); @@ -400,7 +407,7 @@ Test::Result PK_Decryption_Test::run_one_test(const std::string& pad_hdr, const std::unique_ptr decryptor; try { - decryptor = std::make_unique(*privkey, Test::rng(), padding, dec_provider); + decryptor = std::make_unique(*privkey, this->rng(), padding, dec_provider); } catch(Botan::Lookup_Error&) { continue; } @@ -413,7 +420,7 @@ Test::Result PK_Decryption_Test::run_one_test(const std::string& pad_hdr, const } result.test_eq(dec_provider, "decryption of KAT", decrypted, plaintext); - check_invalid_ciphertexts(result, *decryptor, plaintext, ciphertext); + check_invalid_ciphertexts(result, *decryptor, plaintext, ciphertext, this->rng()); } return result; @@ -460,7 +467,7 @@ Test::Result PK_KEM_Test::run_one_test(const std::string& /*header*/, const VarM std::unique_ptr dec; try { - dec = std::make_unique(*privkey, Test::rng(), kdf); + dec = std::make_unique(*privkey, this->rng(), kdf); } catch(Botan::Lookup_Error& e) { result.test_note("Skipping test", e.what()); return result; @@ -500,7 +507,7 @@ Test::Result PK_Key_Agreement_Test::run_one_test(const std::string& header, cons std::unique_ptr kas; try { - kas = std::make_unique(*privkey, Test::rng(), kdf, provider); + kas = std::make_unique(*privkey, this->rng(), kdf, provider); auto derived_key = kas->derive_key(key_len, pubkey).bits_of(); result.test_eq(provider, "agreement", derived_key, shared); @@ -529,12 +536,14 @@ namespace { void test_pbe_roundtrip(Test::Result& result, const Botan::Private_Key& key, const std::string& pbe_algo, - const std::string& passphrase) { + Botan::RandomNumberGenerator& rng) { const auto pkcs8 = key.private_key_info(); + auto passphrase = Test::random_password(rng); + try { Botan::DataSource_Memory data_src( - Botan::PKCS8::PEM_encode(key, Test::rng(), passphrase, std::chrono::milliseconds(1), pbe_algo)); + Botan::PKCS8::PEM_encode(key, rng, passphrase, std::chrono::milliseconds(1), pbe_algo)); auto loaded = Botan::PKCS8::load_key(data_src, passphrase); @@ -547,7 +556,7 @@ void test_pbe_roundtrip(Test::Result& result, try { Botan::DataSource_Memory data_src( - Botan::PKCS8::BER_encode(key, Test::rng(), passphrase, std::chrono::milliseconds(1), pbe_algo)); + Botan::PKCS8::BER_encode(key, rng, passphrase, std::chrono::milliseconds(1), pbe_algo)); auto loaded = Botan::PKCS8::load_key(data_src, passphrase); @@ -578,7 +587,7 @@ std::vector PK_Key_Generation_Test::run() { result.start_timer(); for(auto&& prov : providers) { - auto key_p = Botan::create_private_key(algo_name(), Test::rng(), param, prov); + auto key_p = Botan::create_private_key(algo_name(), this->rng(), param, prov); if(key_p == nullptr) { result.test_failure("create_private_key returned null, should throw instead"); @@ -588,7 +597,7 @@ std::vector PK_Key_Generation_Test::run() { const Botan::Private_Key& key = *key_p; try { - result.confirm("Key passes self tests", key.check_key(Test::rng(), true)); + result.confirm("Key passes self tests", key.check_key(this->rng(), true)); } catch(Botan::Lookup_Error&) {} result.test_gte("Key has reasonable estimated strength (lower)", key.estimated_strength(), 64); @@ -605,7 +614,7 @@ std::vector PK_Key_Generation_Test::run() { // KEX algorithms must support that (so that we can generate ephemeral keys in // an abstract fashion). For other algorithms it's a nice-to-have. try { - auto sk2 = public_key->generate_another(Test::rng()); + auto sk2 = public_key->generate_another(this->rng()); auto pk2 = sk2->public_key(); result.test_eq("new private key has the same name", sk2->algo_name(), key.algo_name()); @@ -630,7 +639,7 @@ std::vector PK_Key_Generation_Test::run() { result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); try { - result.test_eq("public key passes checks", loaded->check_key(Test::rng(), false), true); + result.test_eq("public key passes checks", loaded->check_key(this->rng(), false), true); } catch(Botan::Lookup_Error&) {} } catch(std::exception& e) { result.test_failure("roundtrip PEM public key", e.what()); @@ -674,12 +683,12 @@ std::vector PK_Key_Generation_Test::run() { #if defined(BOTAN_HAS_PKCS5_PBES2) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_SHA2_32) - test_pbe_roundtrip(result, key, "PBE-PKCS5v20(AES-128/CBC,SHA-256)", Test::random_password()); + test_pbe_roundtrip(result, key, "PBE-PKCS5v20(AES-128/CBC,SHA-256)", this->rng()); #endif #if defined(BOTAN_HAS_PKCS5_PBES2) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_SCRYPT) - test_pbe_roundtrip(result, key, "PBES2(AES-128/CBC,Scrypt)", Test::random_password()); + test_pbe_roundtrip(result, key, "PBES2(AES-128/CBC,Scrypt)", this->rng()); #endif } @@ -701,7 +710,7 @@ Test::Result PK_Key_Validity_Test::run_one_test(const std::string& header, const const bool expected_valid = (header == "Valid"); auto pubkey = load_public_key(vars); - const bool tested_valid = pubkey->check_key(rng(), true); + const bool tested_valid = pubkey->check_key(this->rng(), true); result.test_eq("Expected validation result", expected_valid, tested_valid); @@ -789,7 +798,7 @@ class PK_API_Sign_Test : public Text_Based_Test { } Test::Result result(test_name.str()); - auto privkey = Botan::create_private_key(algorithm, Test::rng(), algo_params, provider); + auto privkey = Botan::create_private_key(algorithm, this->rng(), algo_params, provider); if(!privkey) { result.test_note(Botan::fmt( "Skipping Sign/verify API tests for {}({}) with provider {}", algorithm, algo_params, provider)); @@ -812,7 +821,7 @@ class PK_API_Sign_Test : public Text_Based_Test { } auto signer = std::make_unique( - *privkey, Test::rng(), sig_params, Botan::Signature_Format::Standard, provider); + *privkey, this->rng(), sig_params, Botan::Signature_Format::Standard, provider); auto verifier = std::make_unique(*pubkey, verify_params, Botan::Signature_Format::Standard, provider); result.confirm("Creating PK_Signer works", signer != nullptr); @@ -827,7 +836,7 @@ class PK_API_Sign_Test : public Text_Based_Test { pubkey.reset(); privkey.reset(); const std::array msg{0xde, 0xad, 0xbe, 0xef}; - const auto sig = signer->sign_message(msg, Test::rng()); + const auto sig = signer->sign_message(msg, this->rng()); result.test_gt("Signer should still work if no one else hold a reference to the key", sig.size(), 0); result.test_eq("Verifier should still work if no one else hold a reference to the key", verifier->verify_message(msg, sig), diff --git a/src/tests/test_pubkey.h b/src/tests/test_pubkey.h index 2f18fcfd9de..a6ffc9634bf 100644 --- a/src/tests/test_pubkey.h +++ b/src/tests/test_pubkey.h @@ -101,7 +101,7 @@ class PK_Sign_Verify_DER_Test : public Test { protected: std::vector run() final; - virtual std::unique_ptr key() const = 0; + virtual std::unique_ptr key() = 0; virtual bool test_random_invalid_sigs() const { return true; } @@ -214,15 +214,11 @@ class PK_Key_Validity_Test : public PK_Test { Test::Result run_one_test(const std::string& header, const VarMap& vars) final; }; -void check_invalid_signatures(Test::Result& result, - Botan::PK_Verifier& verifier, - const std::vector& message, - const std::vector& signature); - void check_invalid_ciphertexts(Test::Result& result, Botan::PK_Decryptor& decryptor, const std::vector& plaintext, - const std::vector& ciphertext); + const std::vector& ciphertext, + Botan::RandomNumberGenerator& rng); } // namespace Botan_Tests diff --git a/src/tests/test_rng.h b/src/tests/test_rng.h index 06bb6257700..526a51745d4 100644 --- a/src/tests/test_rng.h +++ b/src/tests/test_rng.h @@ -94,17 +94,18 @@ class Fixed_Output_RNG : public Botan::RandomNumberGenerator { */ class Fixed_Output_Position_RNG final : public Fixed_Output_RNG { public: - bool is_seeded() const override { return Fixed_Output_RNG::is_seeded() || Test::rng().is_seeded(); } + // We output either the fixed output, or otherwise random + bool is_seeded() const override { return true; } bool accepts_input() const override { return false; } std::string name() const override { return "Fixed_Output_Position_RNG"; } - explicit Fixed_Output_Position_RNG(const std::vector& in, size_t pos) : - Fixed_Output_RNG(in), m_pos(pos) {} + Fixed_Output_Position_RNG(const std::vector& in, size_t pos, Botan::RandomNumberGenerator& rng) : + Fixed_Output_RNG(in), m_pos(pos), m_rng(rng) {} - explicit Fixed_Output_Position_RNG(const std::string& in_str, size_t pos) : - Fixed_Output_RNG(in_str), m_pos(pos) {} + Fixed_Output_Position_RNG(const std::string& in_str, size_t pos, Botan::RandomNumberGenerator& rng) : + Fixed_Output_RNG(in_str), m_pos(pos), m_rng(rng) {} private: void fill_bytes_with_input(std::span output, std::span input) override { @@ -119,13 +120,14 @@ class Fixed_Output_Position_RNG final : public Fixed_Output_RNG { Fixed_Output_RNG::fill_bytes_with_input(output, input); } else { // return random - Test::rng().random_vec(output); + m_rng.random_vec(output); } } private: size_t m_pos = 0; size_t m_requests = 0; + Botan::RandomNumberGenerator& m_rng; }; class SeedCapturing_RNG final : public Botan::RandomNumberGenerator { diff --git a/src/tests/test_rng_behavior.cpp b/src/tests/test_rng_behavior.cpp index bd39bc3cf4d..141ba844e8a 100644 --- a/src/tests/test_rng_behavior.cpp +++ b/src/tests/test_rng_behavior.cpp @@ -199,7 +199,7 @@ class Stateful_RNG_Tests : public Test { result.test_throws("broken underlying rng but good entropy sources", [&rng_with_broken_rng_and_good_es]() { rng_with_broken_rng_and_good_es->random_vec(16); }); - auto rng_with_good_rng_and_broken_es = make_rng(Test::rng(), broken_entropy_sources); + auto rng_with_good_rng_and_broken_es = make_rng(this->rng(), broken_entropy_sources); result.test_throws("good underlying rng but broken entropy sources", [&rng_with_good_rng_and_broken_es]() { rng_with_good_rng_and_broken_es->random_vec(16); }); diff --git a/src/tests/test_roughtime.cpp b/src/tests/test_roughtime.cpp index 021391ebbe2..9d485bb029a 100644 --- a/src/tests/test_roughtime.cpp +++ b/src/tests/test_roughtime.cpp @@ -107,10 +107,11 @@ class Roughtime_nonce_from_blind_Tests final : public Text_Based_Test { BOTAN_REGISTER_TEST("roughtime", "roughtime_nonce_from_blind", Roughtime_nonce_from_blind_Tests); class Roughtime final : public Test { - static Test::Result test_nonce() { + private: + static Test::Result test_nonce(Botan::RandomNumberGenerator& rng) { Test::Result result("roughtime nonce"); - auto rand64 = Botan::unlock(Test::rng().random_vec(64)); + auto rand64 = Botan::unlock(rng.random_vec(64)); Botan::Roughtime::Nonce nonce_v(rand64); result.confirm("nonce from vector", nonce_v.get_nonce() == Botan::typecast_copy>(rand64.data())); @@ -126,13 +127,13 @@ class Roughtime final : public Test { return result; } - static Test::Result test_chain() { + static Test::Result test_chain(Botan::RandomNumberGenerator& rng) { Test::Result result("roughtime chain"); Botan::Roughtime::Chain c1; result.confirm("default constructed is empty", c1.links().empty() && c1.responses().empty()); - auto rand64 = Botan::unlock(Test::rng().random_vec(64)); + auto rand64 = Botan::unlock(rng.random_vec(64)); Botan::Roughtime::Nonce nonce_v(rand64); result.confirm( "empty chain nonce is blind", @@ -227,10 +228,10 @@ class Roughtime final : public Test { return result; } - static Test::Result test_request_online() { + static Test::Result test_request_online(Botan::RandomNumberGenerator& rng) { Test::Result result("roughtime request online"); - Botan::Roughtime::Nonce nonce(Test::rng()); + Botan::Roughtime::Nonce nonce(rng); try { const auto response_raw = Botan::Roughtime::online_request("roughtime.cloudflare.com:2002", nonce, std::chrono::seconds(5)); @@ -248,13 +249,12 @@ class Roughtime final : public Test { public: std::vector run() override { - std::vector results; - results.push_back(test_nonce()); - results.push_back(test_chain()); - results.push_back(test_server_information()); + auto& rng = this->rng(); + + std::vector results{test_nonce(rng), test_chain(rng), test_server_information()}; if(Test::options().run_online_tests()) { - results.push_back(test_request_online()); + results.push_back(test_request_online(rng)); } return results; diff --git a/src/tests/test_rsa.cpp b/src/tests/test_rsa.cpp index 68ea677e162..3c6d27c05fc 100644 --- a/src/tests/test_rsa.cpp +++ b/src/tests/test_rsa.cpp @@ -182,7 +182,7 @@ class RSA_Blinding_Tests final : public Test { } #if defined(BOTAN_HAS_EMSA_RAW) || defined(BOTAN_HAS_EME_RAW) - Botan::RSA_PrivateKey rsa(Test::rng(), 1024); + Botan::RSA_PrivateKey rsa(this->rng(), 1024); Botan::Null_RNG null_rng; #endif @@ -197,7 +197,7 @@ class RSA_Blinding_Tests final : public Test { */ Botan::PK_Signer signer( - rsa, Test::rng(), "Raw", Botan::Signature_Format::Standard, "base"); // don't try this at home + rsa, this->rng(), "Raw", Botan::Signature_Format::Standard, "base"); // don't try this at home Botan::PK_Verifier verifier(rsa, "Raw", Botan::Signature_Format::Standard, "base"); for(size_t i = 1; i <= BOTAN_BLINDING_REINIT_INTERVAL * 6; ++i) { @@ -223,7 +223,7 @@ class RSA_Blinding_Tests final : public Test { * are used as an additional test on the blinders. */ - Botan::PK_Encryptor_EME encryptor(rsa, Test::rng(), "Raw", "base"); // don't try this at home + Botan::PK_Encryptor_EME encryptor(rsa, this->rng(), "Raw", "base"); // don't try this at home /* Test blinding reinit interval @@ -234,7 +234,7 @@ class RSA_Blinding_Tests final : public Test { */ const size_t rng_bytes = rsa.get_n().bytes() + (2 * 8 * BOTAN_BLINDING_REINIT_INTERVAL); - Botan_Tests::Fixed_Output_RNG fixed_rng(Test::rng(), rng_bytes); + Botan_Tests::Fixed_Output_RNG fixed_rng(this->rng(), rng_bytes); Botan::PK_Decryptor_EME decryptor(rsa, fixed_rng, "Raw", "base"); for(size_t i = 1; i <= BOTAN_BLINDING_REINIT_INTERVAL; ++i) { diff --git a/src/tests/test_sm2.cpp b/src/tests/test_sm2.cpp index 7749b9b6c3c..a95f7d29a50 100644 --- a/src/tests/test_sm2.cpp +++ b/src/tests/test_sm2.cpp @@ -48,7 +48,7 @@ class SM2_Signature_KAT_Tests final : public PK_Signature_Generation_Test { } std::unique_ptr test_rng(const std::vector& nonce) const override { - return std::make_unique(nonce, 1); + return std::make_unique(nonce, 1, this->rng()); } std::unique_ptr load_private_key(const VarMap& vars) override { @@ -69,7 +69,7 @@ class SM2_Encryption_KAT_Tests final : public PK_Encryption_Decryption_Test { bool clear_between_callbacks() const override { return false; } std::unique_ptr test_rng(const std::vector& nonce) const override { - return std::make_unique(nonce, 1); + return std::make_unique(nonce, 1, this->rng()); } std::unique_ptr load_private_key(const VarMap& vars) override { diff --git a/src/tests/test_sphincsplus.cpp b/src/tests/test_sphincsplus.cpp index 095cc34e330..84cc8dbf7b2 100644 --- a/src/tests/test_sphincsplus.cpp +++ b/src/tests/test_sphincsplus.cpp @@ -142,7 +142,7 @@ class SPHINCS_Plus_Test final : public Text_Based_Test { result.confirm("verification of valid signature after deserialization", verify_success_deserialized); // Verification of invalid signature - auto broken_sig = Test::mutate_vec(deserialized_signature); + auto broken_sig = Test::mutate_vec(deserialized_signature, this->rng()); bool verify_fail = deserialized_verifier.verify_message( msg_ref.data(), msg_ref.size(), broken_sig.data(), broken_sig.size()); result.confirm("verification of invalid signature", !verify_fail); diff --git a/src/tests/test_srp6.cpp b/src/tests/test_srp6.cpp index 959c1accd35..ba6059f47a3 100644 --- a/src/tests/test_srp6.cpp +++ b/src/tests/test_srp6.cpp @@ -109,16 +109,16 @@ class SRP6_RT_Tests final : public Test { for(size_t t = 0; t != trials; ++t) { std::vector salt; - Test::rng().random_vec(salt, 16); + this->rng().random_vec(salt, 16); const Botan::BigInt verifier = Botan::srp6_generate_verifier(username, password, salt, group_id, hash_id); Botan::SRP6_Server_Session server; - const Botan::BigInt B = server.step1(verifier, group_id, hash_id, Test::rng()); + const Botan::BigInt B = server.step1(verifier, group_id, hash_id, this->rng()); - auto client = srp6_client_agree(username, password, group_id, hash_id, salt, B, Test::rng()); + auto client = srp6_client_agree(username, password, group_id, hash_id, salt, B, this->rng()); const Botan::SymmetricKey server_K = server.step2(client.first); diff --git a/src/tests/test_stream.cpp b/src/tests/test_stream.cpp index beb4f2fed73..dc12c3dfe0f 100644 --- a/src/tests/test_stream.cpp +++ b/src/tests/test_stream.cpp @@ -133,7 +133,7 @@ class Stream_Cipher_Tests final : public Text_Based_Test { auto clone = cipher->new_object(); result.confirm("Clone has different pointer", cipher.get() != clone.get()); result.test_eq("Clone has same name", cipher->name(), clone->name()); - clone->set_key(Test::rng().random_vec(cipher->maximum_keylength())); + clone->set_key(this->rng().random_vec(cipher->maximum_keylength())); { std::vector buf = input; @@ -180,7 +180,7 @@ class Stream_Cipher_Tests final : public Text_Based_Test { size_t buf_len = buf.size(); while(buf_len > 0) { - size_t next = std::min(buf_len, rng().next_byte()); + size_t next = std::min(buf_len, this->rng().next_byte()); cipher->write_keystream(buf_ptr, next); buf_ptr += next; buf_len -= next; diff --git a/src/tests/test_tests.cpp b/src/tests/test_tests.cpp index 5efb5e3232d..39937cb6867 100644 --- a/src/tests/test_tests.cpp +++ b/src/tests/test_tests.cpp @@ -207,8 +207,10 @@ class Test_Tests final : public Test { const size_t RUNS = 1000; + auto rng = Test::new_rng(__func__); + for(size_t i = 0; i != 256 * RUNS; ++i) { - histogram[rng().next_byte()] += 1; + histogram[rng->next_byte()] += 1; } for(size_t i = 0; i != 256; ++i) { diff --git a/src/tests/test_tls.cpp b/src/tests/test_tls.cpp index c2ffc25e54f..6f8c5a41b2d 100644 --- a/src/tests/test_tls.cpp +++ b/src/tests/test_tls.cpp @@ -51,8 +51,8 @@ class TLS_Session_Tests final : public Test { result.test_eq("Roundtrip from der", session.DER_encode(), session_from_der.DER_encode()); const Botan::SymmetricKey key("ABCDEF"); - const std::vector ctext1 = session.encrypt(key, Test::rng()); - const std::vector ctext2 = session.encrypt(key, Test::rng()); + const std::vector ctext1 = session.encrypt(key, this->rng()); + const std::vector ctext2 = session.encrypt(key, this->rng()); result.test_ne( "TLS session encryption is non-determinsitic", ctext1.data(), ctext1.size(), ctext2.data(), ctext2.size()); diff --git a/src/tests/test_tls_hybrid_kem_key.cpp b/src/tests/test_tls_hybrid_kem_key.cpp index ebd400b2225..1aa5f65de86 100644 --- a/src/tests/test_tls_hybrid_kem_key.cpp +++ b/src/tests/test_tls_hybrid_kem_key.cpp @@ -19,18 +19,25 @@ namespace Botan_Tests { namespace { +// For convenience, we register a test-global RNG instance at the beginning of +// this test suite. This RNG instance is used by all test cases in this file. +Botan::RandomNumberGenerator& global_test_rng() { + static auto test_global_rng = Test::new_rng(__func__); + return *test_global_rng; +} + // The concrete key pairs are not relevant for these test cases. For convenience // and performance reasons, kem(), kex_dh(), kex_ecdh(), and sig() generate only // a single key pair and return a copy of it with every invocation. Note that // the tests assume that those methods always return the same key. std::unique_ptr kem() { - static auto kem_key = Botan::create_private_key("Kyber", Test::rng(), "Kyber-512-r3"); + static auto kem_key = Botan::create_private_key("Kyber", global_test_rng(), "Kyber-512-r3"); return Botan::load_private_key(kem_key->algorithm_identifier(), kem_key->private_key_bits()); } std::unique_ptr kex_dh() { - static auto kex_key = Botan::create_private_key("DH", Test::rng(), "ffdhe/ietf/2048"); + static auto kex_key = Botan::create_private_key("DH", global_test_rng(), "ffdhe/ietf/2048"); auto sk = Botan::load_private_key(kex_key->algorithm_identifier(), kex_key->private_key_bits()); auto kex_sk = dynamic_cast(sk.get()); if(kex_sk) { @@ -42,7 +49,7 @@ std::unique_ptr kex_dh() { } std::unique_ptr kex_ecdh() { - static auto kex_key = Botan::create_private_key("ECDH", Test::rng(), "secp256r1"); + static auto kex_key = Botan::create_private_key("ECDH", global_test_rng(), "secp256r1"); auto sk = Botan::load_private_key(kex_key->algorithm_identifier(), kex_key->private_key_bits()); auto kex_sk = dynamic_cast(sk.get()); if(kex_sk) { @@ -54,7 +61,7 @@ std::unique_ptr kex_ecdh() { } std::unique_ptr sig() { - static auto sig_key = Botan::create_private_key("ECDSA", Test::rng(), "secp256r1"); + static auto sig_key = Botan::create_private_key("ECDSA", global_test_rng(), "secp256r1"); return Botan::load_private_key(sig_key->algorithm_identifier(), sig_key->private_key_bits()); } @@ -84,7 +91,7 @@ auto pubkeys(KeyTs... keys) { template size_t length_of_hybrid_shared_key(Ts... kex_kem_fn) { Botan::overloaded f{[](const Botan::PK_Key_Agreement_Key& kex_key) { - Botan::PK_Key_Agreement ka(kex_key, Test::rng(), "Raw"); + Botan::PK_Key_Agreement ka(kex_key, global_test_rng(), "Raw"); return ka.agreed_value_size(); }, [](const Botan::Private_Key& kem_key) { @@ -119,7 +126,7 @@ void roundtrip_test(Test::Result& result, Ts... kex_kem_fn) { Botan::TLS::Hybrid_KEM_PrivateKey hybrid_key(keys(kex_kem_fn()...)); Botan::TLS::Hybrid_KEM_PublicKey hybrid_public_key(pubkeys(kex_kem_fn()...)); - auto& rng = Test::rng(); + auto& rng = global_test_rng(); Botan::PK_KEM_Encryptor encryptor(hybrid_public_key, "Raw"); const auto kem_result = encryptor.encrypt(rng); @@ -209,7 +216,7 @@ void kex_to_kem_roundtrip(Test::Result& result, Botan::TLS::KEX_to_KEM_Adapter_PrivateKey kexkem_key(kex_fn()); Botan::TLS::KEX_to_KEM_Adapter_PublicKey kexkem_public_key(kex_fn()); - auto& rng = Test::rng(); + auto& rng = global_test_rng(); Botan::PK_KEM_Encryptor encryptor(kexkem_public_key, "Raw"); const auto kem_result = encryptor.encrypt(rng); diff --git a/src/tests/test_tls_rfc8448.cpp b/src/tests/test_tls_rfc8448.cpp index cb5eb40afd5..3200f3e9154 100644 --- a/src/tests/test_tls_rfc8448.cpp +++ b/src/tests/test_tls_rfc8448.cpp @@ -376,9 +376,10 @@ class Test_Credentials : public Botan::Credentials_Manager { // fly as a stand-in. Instead of actually using it, the signatures generated // by this private key must be hard-coded in `Callbacks::sign_message()`; see // `MockSignature_Fn` for more details. - m_bogus_alternative_server_private_key.reset(create_private_key("ECDSA", Test::rng(), "secp256r1").release()); + auto rng = Test::new_rng(__func__); + m_bogus_alternative_server_private_key.reset(create_private_key("ECDSA", *rng, "secp256r1").release()); - m_client_private_key.reset(create_private_key("RSA", Test::rng(), "1024").release()); + m_client_private_key.reset(create_private_key("RSA", *rng, "1024").release()); } std::vector cert_chain(const std::vector& cert_key_types, @@ -552,7 +553,7 @@ class RFC8448_Session_Manager : public Botan::TLS::Session_Manager { } public: - RFC8448_Session_Manager() : Session_Manager(Test::rng_as_shared()) {} + RFC8448_Session_Manager() : Session_Manager(std::make_shared()) {} const std::vector& all_sessions() const { return m_sessions; } @@ -1209,7 +1210,7 @@ class Test_TLS_RFC8448_Client : public Test_TLS_RFC8448 { }; // Fallback RNG is required to for blinding in ECDH with P-256 - auto& fallback_rng = Test::rng(); + auto& fallback_rng = this->rng(); auto rng = std::make_unique(fallback_rng); // 32 - client hello random @@ -2065,7 +2066,7 @@ class Test_TLS_RFC8448_Server : public Test_TLS_RFC8448 { std::vector hello_retry_request(const VarMap& vars) override { // Fallback RNG is required to for blinding in ECDH with P-256 - auto& fallback_rng = Test::rng(); + auto& fallback_rng = this->rng(); auto rng = std::make_unique(fallback_rng); // 32 - for server hello random diff --git a/src/tests/test_tls_session_manager.cpp b/src/tests/test_tls_session_manager.cpp index d20875dbfa9..4ed74b525f2 100644 --- a/src/tests/test_tls_session_manager.cpp +++ b/src/tests/test_tls_session_manager.cpp @@ -96,16 +96,16 @@ class Session_Manager_Policy : public Botan::TLS::Policy { namespace { -decltype(auto) random_id() { - return Test::rng().random_vec(32); +decltype(auto) random_id(Botan::RandomNumberGenerator& rng) { + return rng.random_vec(32); } -decltype(auto) random_ticket() { - return Test::rng().random_vec(32); +decltype(auto) random_ticket(Botan::RandomNumberGenerator& rng) { + return rng.random_vec(32); } -decltype(auto) random_opaque_handle() { - return Test::rng().random_vec(32); +decltype(auto) random_opaque_handle(Botan::RandomNumberGenerator& rng) { + return rng.random_vec(32); } const Botan::TLS::Server_Information server_info("botan.randombit.net"); @@ -138,7 +138,9 @@ decltype(auto) default_session(Botan::TLS::Connection_Side side, using namespace std::literals; std::vector test_session_manager_in_memory() { - const Botan::TLS::Session_ID default_id = random_id(); + auto rng = Test::new_shared_rng(__func__); + + const Botan::TLS::Session_ID default_id = random_id(*rng); std::optional mgr; @@ -146,15 +148,15 @@ std::vector test_session_manager_in_memory() { Session_Manager_Policy plcy; return { - Botan_Tests::CHECK("creation", [&](auto&) { mgr.emplace(Test::rng_as_shared(), 5); }), + Botan_Tests::CHECK("creation", [&](auto&) { mgr.emplace(rng, 5); }), Botan_Tests::CHECK("empty cache does not obtain anything", [&](auto& result) { result.confirm("no session found via server info", mgr->find(server_info, cbs, plcy).empty()); - Botan::TLS::Session_ID mock_id = random_id(); - auto mock_ticket = Test::rng().random_vec(128); + Botan::TLS::Session_ID mock_id = random_id(*rng); + auto mock_ticket = rng->random_vec(128); result.confirm("no session found via ID", !mgr->retrieve(mock_id, cbs, plcy)); result.confirm("no session found via ID", !mgr->retrieve(mock_ticket, cbs, plcy)); @@ -220,13 +222,13 @@ std::vector test_session_manager_in_memory() { Botan_Tests::CHECK("invalid ticket causes std::nullopt", [&](auto& result) { - auto no_session = mgr->retrieve(random_ticket(), cbs, plcy); + auto no_session = mgr->retrieve(random_ticket(*rng), cbs, plcy); result.confirm("std::nullopt on bogus ticket", !no_session.has_value()); }), Botan_Tests::CHECK("invalid ID causes std::nullopt", [&](auto& result) { - auto no_session = mgr->retrieve(random_id(), cbs, plcy); + auto no_session = mgr->retrieve(random_id(*rng), cbs, plcy); result.confirm("std::nullopt on bogus ID", !no_session.has_value()); }), @@ -238,7 +240,7 @@ std::vector test_session_manager_in_memory() { Botan_Tests::CHECK("add session with ID", [&](auto& result) { - Botan::TLS::Session_ID new_id = random_id(); + Botan::TLS::Session_ID new_id = random_id(*rng); mgr->store(default_session(Botan::TLS::Connection_Side::Client, cbs), new_id); result.require("obtain via ID", mgr->retrieve(new_id, cbs, plcy).has_value()); @@ -259,7 +261,7 @@ std::vector test_session_manager_in_memory() { Botan_Tests::CHECK("add session with ticket", [&](auto& result) { - Botan::TLS::Session_Ticket new_ticket = random_ticket(); + Botan::TLS::Session_Ticket new_ticket = random_ticket(*rng); mgr->store(default_session(Botan::TLS::Connection_Side::Client, cbs), new_ticket); // cannot be obtained by (non-existent) ID or randomly generated ticket @@ -281,7 +283,7 @@ std::vector test_session_manager_in_memory() { Botan_Tests::CHECK( "removing by ID or opaque handle", [&](auto& result) { - Botan::TLS::Session_Manager_In_Memory local_mgr(Test::rng_as_shared()); + Botan::TLS::Session_Manager_In_Memory local_mgr(rng); const auto new_session1 = local_mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), default_id); @@ -312,11 +314,11 @@ std::vector test_session_manager_in_memory() { Botan_Tests::CHECK( "removing by ticket or opaque handle", [&](auto& result) { - Botan::TLS::Session_Manager_In_Memory local_mgr(Test::rng_as_shared()); + Botan::TLS::Session_Manager_In_Memory local_mgr(rng); - Botan::TLS::Session_Ticket ticket1 = random_ticket(); - Botan::TLS::Session_Ticket ticket2 = random_ticket(); - Botan::TLS::Session_Ticket ticket3 = random_ticket(); + Botan::TLS::Session_Ticket ticket1 = random_ticket(*rng); + Botan::TLS::Session_Ticket ticket2 = random_ticket(*rng); + Botan::TLS::Session_Ticket ticket3 = random_ticket(*rng); local_mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), ticket1); local_mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), ticket2); @@ -343,7 +345,7 @@ std::vector test_session_manager_in_memory() { std::vector handles; for(size_t i = 0; i < mgr->capacity(); ++i) { handles.push_back( - mgr->establish(default_session(Botan::TLS::Connection_Side::Server, cbs), random_id()).value()); + mgr->establish(default_session(Botan::TLS::Connection_Side::Server, cbs), random_id(*rng)).value()); } for(size_t i = 0; i < handles.size(); ++i) { @@ -352,7 +354,7 @@ std::vector test_session_manager_in_memory() { // add one more session (causing a first purge to happen) handles.push_back( - mgr->establish(default_session(Botan::TLS::Connection_Side::Server, cbs), random_id()).value()); + mgr->establish(default_session(Botan::TLS::Connection_Side::Server, cbs), random_id(*rng)).value()); result.confirm("oldest session gone", !mgr->retrieve(handles[0], cbs, plcy).has_value()); for(size_t i = 1; i < handles.size(); ++i) { @@ -370,7 +372,7 @@ std::vector test_session_manager_in_memory() { // insert enough new sessions to fully purge the ones currently held for(size_t i = 0; i < mgr->capacity(); ++i) { handles.push_back( - mgr->establish(default_session(Botan::TLS::Connection_Side::Server, cbs), random_id()).value()); + mgr->establish(default_session(Botan::TLS::Connection_Side::Server, cbs), random_id(*rng)).value()); } for(size_t i = 0; i < handles.size() - mgr->capacity(); ++i) { @@ -392,6 +394,8 @@ std::vector test_session_manager_choose_ticket() { Session_Manager_Callbacks cbs; Session_Manager_Policy plcy; + auto rng = Test::new_shared_rng(__func__); + auto default_session = [&](const std::string& suite, Botan::TLS::Callbacks& mycbs, Botan::TLS::Protocol_Version version = Botan::TLS::Protocol_Version::TLS_V13) { @@ -426,9 +430,9 @@ std::vector test_session_manager_choose_ticket() { return { CHECK("empty manager has nothing to choose from", [&](auto& result) { - Botan::TLS::Session_Manager_In_Memory mgr(Test::rng_as_shared()); + Botan::TLS::Session_Manager_In_Memory mgr(rng); - Botan::TLS::Session_Ticket random_session_ticket = random_ticket(); + Botan::TLS::Session_Ticket random_session_ticket = random_ticket(*rng); result.confirm("empty ticket list, no session", !mgr.choose_from_offered_tickets({}, "SHA-256", cbs, plcy).has_value()); @@ -440,7 +444,7 @@ std::vector test_session_manager_choose_ticket() { CHECK("choose ticket by ID", [&](auto& result) { - Botan::TLS::Session_Manager_In_Memory mgr(Test::rng_as_shared()); + Botan::TLS::Session_Manager_In_Memory mgr(rng); std::vector handles; handles.push_back(mgr.establish(default_session("AES_128_GCM_SHA256", cbs)).value()); @@ -460,7 +464,7 @@ std::vector test_session_manager_choose_ticket() { // choose from a list of tickets that contains a random ticket and handles[1] auto session3 = mgr.choose_from_offered_tickets( - std::vector{ticket(random_ticket()), ticket(handles[1].id().value())}, "SHA-256", cbs, plcy); + std::vector{ticket(random_ticket(*rng)), ticket(handles[1].id().value())}, "SHA-256", cbs, plcy); result.require("ticket was chosen and produced a session (3)", session3.has_value()); result.test_is_eq("chosen second offset", session3->second, uint16_t(1)); }), @@ -468,7 +472,7 @@ std::vector test_session_manager_choose_ticket() { CHECK("choose ticket by ticket", [&](auto& result) { auto creds = std::make_shared(); - Botan::TLS::Session_Manager_Stateless mgr(creds, Test::rng_as_shared()); + Botan::TLS::Session_Manager_Stateless mgr(creds, rng); std::vector handles; handles.push_back(mgr.establish(default_session("AES_128_GCM_SHA256", cbs)).value()); @@ -488,7 +492,7 @@ std::vector test_session_manager_choose_ticket() { // choose from a list of tickets that contains a random ticket and handles[1] auto session3 = mgr.choose_from_offered_tickets( - std::vector{ticket(random_ticket()), ticket(handles[1].ticket().value())}, "SHA-256", cbs, plcy); + std::vector{ticket(random_ticket(*rng)), ticket(handles[1].ticket().value())}, "SHA-256", cbs, plcy); result.require("ticket was chosen and produced a session (3)", session3.has_value()); result.test_is_eq("chosen second offset", session3->second, uint16_t(1)); }), @@ -496,18 +500,18 @@ std::vector test_session_manager_choose_ticket() { CHECK("choose ticket based on requested hash function", [&](auto& result) { auto creds = std::make_shared(); - Botan::TLS::Session_Manager_Stateless mgr(creds, Test::rng_as_shared()); + Botan::TLS::Session_Manager_Stateless mgr(creds, rng); std::vector handles; handles.push_back(mgr.establish(default_session("AES_128_GCM_SHA256", cbs)).value()); handles.push_back(mgr.establish(default_session("AES_256_GCM_SHA384", cbs)).value()); - auto session = mgr.choose_from_offered_tickets( - std::vector{ - ticket(random_ticket()), ticket(handles[0].ticket().value()), ticket(handles[1].ticket().value())}, - "SHA-384", - cbs, - plcy); + auto session = mgr.choose_from_offered_tickets(std::vector{ticket(random_ticket(*rng)), + ticket(handles[0].ticket().value()), + ticket(handles[1].ticket().value())}, + "SHA-384", + cbs, + plcy); result.require("ticket was chosen and produced a session", session.has_value()); result.test_is_eq("chosen second offset", session->second, uint16_t(2)); }), @@ -515,7 +519,7 @@ std::vector test_session_manager_choose_ticket() { CHECK("choose ticket based on protocol version", [&](auto& result) { auto creds = std::make_shared(); - Botan::TLS::Session_Manager_Stateless mgr(creds, Test::rng_as_shared()); + Botan::TLS::Session_Manager_Stateless mgr(creds, rng); std::vector handles; handles.push_back( @@ -523,12 +527,12 @@ std::vector test_session_manager_choose_ticket() { handles.push_back( mgr.establish(default_session("AES_128_GCM_SHA256", cbs, Botan::TLS::Version_Code::TLS_V13)).value()); - auto session = mgr.choose_from_offered_tickets( - std::vector{ - ticket(random_ticket()), ticket(handles[0].ticket().value()), ticket(handles[1].ticket().value())}, - "SHA-256", - cbs, - plcy); + auto session = mgr.choose_from_offered_tickets(std::vector{ticket(random_ticket(*rng)), + ticket(handles[0].ticket().value()), + ticket(handles[1].ticket().value())}, + "SHA-256", + cbs, + plcy); result.require("ticket was chosen and produced a session", session.has_value()); result.test_is_eq("chosen second offset (TLS 1.3 ticket)", session->second, uint16_t(2)); }), @@ -540,7 +544,10 @@ std::vector test_session_manager_choose_ticket() { std::vector test_session_manager_stateless() { auto creds = std::make_shared(); - Botan::TLS::Session_Manager_Stateless mgr(creds, Test::rng_as_shared()); + + auto rng = Test::new_shared_rng(__func__); + + Botan::TLS::Session_Manager_Stateless mgr(creds, rng); Session_Manager_Callbacks cbs; Session_Manager_Policy plcy; @@ -561,16 +568,15 @@ std::vector test_session_manager_stateless() { result.confirm("returned std::nullopt", !ticket.has_value()); }), - Botan_Tests::CHECK("establish without ticket key in credentials manager", - [&](auto& result) { - Botan::TLS::Session_Manager_Stateless local_mgr( - std::make_shared(), Test::rng_as_shared()); + Botan_Tests::CHECK( + "establish without ticket key in credentials manager", + [&](auto& result) { + Botan::TLS::Session_Manager_Stateless local_mgr(std::make_shared(), rng); - result.confirm("won't emit tickets", !local_mgr.emits_session_tickets()); - auto ticket = - local_mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); - result.confirm("returned std::nullopt", !ticket.has_value()); - }), + result.confirm("won't emit tickets", !local_mgr.emits_session_tickets()); + auto ticket = local_mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); + result.confirm("returned std::nullopt", !ticket.has_value()); + }), Botan_Tests::CHECK("retrieve via ticket", [&](auto& result) { @@ -578,7 +584,7 @@ std::vector test_session_manager_stateless() { auto ticket2 = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); result.require("tickets created successfully", ticket1.has_value() && ticket2.has_value()); - Botan::TLS::Session_Manager_Stateless local_mgr(creds, Test::rng_as_shared()); + Botan::TLS::Session_Manager_Stateless local_mgr(creds, rng); result.confirm("can retrieve ticket 1", mgr.retrieve(ticket1.value(), cbs, plcy).has_value()); result.confirm("can retrieve ticket 2 from different manager but sam credentials", @@ -591,7 +597,7 @@ std::vector test_session_manager_stateless() { result.require("tickets created successfully", ticket1.has_value() && ticket1.has_value()); result.confirm("retrieval by ID does not work", - !mgr.retrieve(random_id(), cbs, plcy).has_value()); + !mgr.retrieve(random_id(*rng), cbs, plcy).has_value()); }), Botan_Tests::CHECK("retrieve via opaque handle does work", @@ -603,42 +609,40 @@ std::vector test_session_manager_stateless() { mgr.retrieve(ticket1->opaque_handle(), cbs, plcy).has_value()); }), - Botan_Tests::CHECK("no retrieve without or with wrong ticket key", - [&](auto& result) { - auto ticket1 = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); - result.require("tickets created successfully", ticket1.has_value() && ticket1.has_value()); + Botan_Tests::CHECK( + "no retrieve without or with wrong ticket key", + [&](auto& result) { + auto ticket1 = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); + result.require("tickets created successfully", ticket1.has_value() && ticket1.has_value()); - Botan::TLS::Session_Manager_Stateless local_mgr1( - std::make_shared(), Test::rng_as_shared()); + Botan::TLS::Session_Manager_Stateless local_mgr1(std::make_shared(), rng); - Botan::TLS::Session_Manager_Stateless local_mgr2( - std::make_shared(), Test::rng_as_shared()); + Botan::TLS::Session_Manager_Stateless local_mgr2(std::make_shared(), rng); - result.confirm("no successful retrieval (without key)", - !local_mgr1.retrieve(ticket1.value(), cbs, plcy).has_value()); - result.confirm("no successful retrieval (with wrong key)", - !local_mgr2.retrieve(ticket1.value(), cbs, plcy).has_value()); - result.confirm("successful retrieval", - mgr.retrieve(ticket1.value(), cbs, plcy).has_value()); - }), + result.confirm("no successful retrieval (without key)", + !local_mgr1.retrieve(ticket1.value(), cbs, plcy).has_value()); + result.confirm("no successful retrieval (with wrong key)", + !local_mgr2.retrieve(ticket1.value(), cbs, plcy).has_value()); + result.confirm("successful retrieval", mgr.retrieve(ticket1.value(), cbs, plcy).has_value()); + }), - Botan_Tests::CHECK("Clients cannot be stateless", - [&](auto& result) { - result.test_throws("::store() does not work with ID", [&] { - mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), random_id()); - }); - result.test_throws("::store() does not work with ticket", [&] { - mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), random_ticket()); - }); - result.test_throws("::store() does not work with opaque handle", [&] { - mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), - random_opaque_handle()); - }); + Botan_Tests::CHECK( + "Clients cannot be stateless", + [&](auto& result) { + result.test_throws("::store() does not work with ID", [&] { + mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), random_id(*rng)); + }); + result.test_throws("::store() does not work with ticket", [&] { + mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), random_ticket(*rng)); + }); + result.test_throws("::store() does not work with opaque handle", [&] { + mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), random_opaque_handle(*rng)); + }); - auto ticket1 = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); - result.require("tickets created successfully", ticket1.has_value() && ticket1.has_value()); - result.confirm("finding tickets does not work", mgr.find(server_info, cbs, plcy).empty()); - }), + auto ticket1 = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); + result.require("tickets created successfully", ticket1.has_value() && ticket1.has_value()); + result.confirm("finding tickets does not work", mgr.find(server_info, cbs, plcy).empty()); + }), Botan_Tests::CHECK( "remove is a NOOP", @@ -672,6 +676,8 @@ std::vector test_session_manager_stateless() { } std::vector test_session_manager_hybrid() { + auto rng = Test::new_shared_rng(__func__); + auto creds = std::make_shared(); Session_Manager_Callbacks cbs; Session_Manager_Policy plcy; @@ -684,14 +690,14 @@ std::vector test_session_manager_hybrid() { std::vector()>>> stateful_manager_factories = { {"In Memory", - []() -> std::unique_ptr { - return std::make_unique(Test::rng_as_shared(), 10); + [&rng]() -> std::unique_ptr { + return std::make_unique(rng, 10); }}, #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) {"SQLite", - []() -> std::unique_ptr { + [&rng]() -> std::unique_ptr { return std::make_unique( - "secure_pw", Test::rng_as_shared(), Test::temp_file_name("tls_session_manager_sqlite"), 10); + "secure_pw", rng, Test::temp_file_name("tls_session_manager_sqlite"), 10); }}, #endif }; @@ -701,9 +707,8 @@ std::vector test_session_manager_hybrid() { for(auto& factory_and_name : stateful_manager_factories) { auto& stateful_manager_name = factory_and_name.first; auto& stateful_manager_factory = factory_and_name.second; - auto make_manager = [stateful_manager_factory, &creds](bool prefer_tickets) { - return Botan::TLS::Session_Manager_Hybrid( - stateful_manager_factory(), creds, Test::rng_as_shared(), prefer_tickets); + auto make_manager = [stateful_manager_factory, &creds, &rng](bool prefer_tickets) { + return Botan::TLS::Session_Manager_Hybrid(stateful_manager_factory(), creds, rng, prefer_tickets); }; auto nm = Botan::fmt("{} ({})", name, stateful_manager_name); @@ -761,9 +766,9 @@ std::vector test_session_manager_hybrid() { CHECK_all("no session tickets if hybrid manager cannot create them", [&](auto make_manager, auto& result) { Botan::TLS::Session_Manager_Hybrid empty_mgr( - std::make_unique(Test::rng_as_shared(), 10), + std::make_unique(rng, 10), std::make_shared(), - Test::rng_as_shared()); + rng); auto mgr_prefers_tickets = make_manager(true); auto mgr_prefers_ids = make_manager(false); @@ -807,6 +812,7 @@ class Temporary_Database_File { std::vector test_session_manager_sqlite() { #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) + auto rng = Test::new_shared_rng(__func__); Session_Manager_Callbacks cbs; Session_Manager_Policy plcy; @@ -820,7 +826,7 @@ std::vector test_session_manager_sqlite() { // $ sqlite3 src/tests/data/tls-sessions/botan-2.19.3.sqlite 'SELECT * FROM tls_sessions;' // 63C136FAD49F05A184F910FD6568A3884164216C11E41CEBFDCD149AF66C1714|1673606906|cloudflare.com|443|... // 63C137030387E4A6CDAD303CCB1F53884944FDE5B4EDD91E6FCF74DCB033DCEB|1673606915|randombit.net|443|... - Botan::TLS::Session_Manager_SQLite legacy_db("thetruthisoutthere", Test::rng_as_shared(), dbfile.get()); + Botan::TLS::Session_Manager_SQLite legacy_db("thetruthisoutthere", rng, dbfile.get()); result.confirm("Session_ID for randombit.net is gone", !legacy_db @@ -847,7 +853,7 @@ std::vector test_session_manager_sqlite() { Botan_Tests::CHECK("clearing empty database", [&](auto& result) { Botan::TLS::Session_Manager_SQLite mgr( - "thetruthisoutthere", Test::rng_as_shared(), Test::temp_file_name("empty.sqlite")); + "thetruthisoutthere", rng, Test::temp_file_name("empty.sqlite")); result.test_eq("does not delete anything", mgr.remove_all(), 0); }), @@ -855,8 +861,8 @@ std::vector test_session_manager_sqlite() { "establish new session", [&](auto& result) { Botan::TLS::Session_Manager_SQLite mgr( - "thetruthisoutthere", Test::rng_as_shared(), Test::temp_file_name("new_session.sqlite")); - auto some_random_id = random_id(); + "thetruthisoutthere", rng, Test::temp_file_name("new_session.sqlite")); + auto some_random_id = random_id(*rng); auto some_random_handle = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), some_random_id); result.require("establishment was successful", some_random_handle.has_value()); @@ -872,8 +878,8 @@ std::vector test_session_manager_sqlite() { "retrieve session by ID", [&](auto& result) { Botan::TLS::Session_Manager_SQLite mgr( - "thetruthisoutthere", Test::rng_as_shared(), Test::temp_file_name("retrieve_by_id.sqlite")); - auto some_random_id = random_id(); + "thetruthisoutthere", rng, Test::temp_file_name("retrieve_by_id.sqlite")); + auto some_random_id = random_id(*rng); auto some_random_handle = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), some_random_id); auto some_virtual_handle = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); @@ -896,7 +902,7 @@ std::vector test_session_manager_sqlite() { result.test_is_eq("ciphersuite was echoed", session2->ciphersuite_code(), uint16_t(0x009C)); } - auto session3 = mgr.retrieve(random_id(), cbs, plcy); + auto session3 = mgr.retrieve(random_id(*rng), cbs, plcy); result.confirm("random ID creates empty result", !session3.has_value()); }), @@ -904,94 +910,93 @@ std::vector test_session_manager_sqlite() { "retrieval via ticket creates empty result", [&](auto& result) { Botan::TLS::Session_Manager_SQLite mgr( - "thetruthisoutthere", Test::rng_as_shared(), Test::temp_file_name("retrieve_by_ticket.sqlite")); + "thetruthisoutthere", rng, Test::temp_file_name("retrieve_by_ticket.sqlite")); auto some_random_handle = - mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), random_id()); + mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), random_id(*rng)); auto some_virtual_handle = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); - result.confirm("std::nullopt on random ticket", !mgr.retrieve(random_ticket(), cbs, plcy).has_value()); + result.confirm("std::nullopt on random ticket", !mgr.retrieve(random_ticket(*rng), cbs, plcy).has_value()); }), + Botan_Tests::CHECK("storing sessions and finding them by server info", + [&](auto& result) { + Botan::TLS::Session_Manager_SQLite mgr( + "thetruthisoutthere", rng, Test::temp_file_name("store_and_find.sqlite")); + auto id = random_id(*rng); + auto ticket = random_ticket(*rng); + mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), id); + mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), ticket); + + auto found_sessions = mgr.find(server_info, cbs, plcy); + if(result.test_is_eq("found both sessions", found_sessions.size(), size_t(2))) { + for(const auto& [session, handle] : found_sessions) { + result.confirm("ID matches", !handle.is_id() || handle.id().value() == id); + result.confirm("ticket matches", + !handle.is_ticket() || handle.ticket().value() == ticket); + } + } + }), + Botan_Tests::CHECK( - "storing sessions and finding them by server info", + "removing sessions", [&](auto& result) { - Botan::TLS::Session_Manager_SQLite mgr( - "thetruthisoutthere", Test::rng_as_shared(), Test::temp_file_name("store_and_find.sqlite")); - auto id = random_id(); - auto ticket = random_ticket(); + Botan::TLS::Session_Manager_SQLite mgr("thetruthisoutthere", rng, Test::temp_file_name("remove.sqlite")); + auto id = random_id(*rng); + auto ticket = random_ticket(*rng); mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), id); mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), ticket); + mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), random_id(*rng)); + mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), random_ticket(*rng)); + + result.test_is_eq("deletes one session by ID", mgr.remove(id), size_t(1)); + result.test_is_eq("deletes one session by ticket", mgr.remove(ticket), size_t(1)); auto found_sessions = mgr.find(server_info, cbs, plcy); - if(result.test_is_eq("found both sessions", found_sessions.size(), size_t(2))) { + if(result.test_is_eq("found some other sessions", found_sessions.size(), size_t(2))) { for(const auto& [session, handle] : found_sessions) { - result.confirm("ID matches", !handle.is_id() || handle.id().value() == id); - result.confirm("ticket matches", !handle.is_ticket() || handle.ticket().value() == ticket); + result.confirm("ID does not match", !handle.is_id() || handle.id().value() != id); + result.confirm("ticket does not match", !handle.is_ticket() || handle.ticket().value() != ticket); } } + + result.test_is_eq("removing the rest of the sessions", mgr.remove_all(), size_t(2)); }), - Botan_Tests::CHECK("removing sessions", - [&](auto& result) { - Botan::TLS::Session_Manager_SQLite mgr( - "thetruthisoutthere", Test::rng_as_shared(), Test::temp_file_name("remove.sqlite")); - auto id = random_id(); - auto ticket = random_ticket(); - mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), id); - mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), ticket); - mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), random_id()); - mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), random_ticket()); + Botan_Tests::CHECK( + "old sessions are purged when needed", + [&](auto& result) { + Botan::TLS::Session_Manager_SQLite mgr( + "thetruthisoutthere", rng, Test::temp_file_name("purging.sqlite"), 1); - result.test_is_eq("deletes one session by ID", mgr.remove(id), size_t(1)); - result.test_is_eq("deletes one session by ticket", mgr.remove(ticket), size_t(1)); + std::vector ids = {random_id(*rng), random_id(*rng), random_id(*rng)}; + mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), ids[0]); + result.require("new ID exists", mgr.retrieve(ids[0], cbs, plcy).has_value()); - auto found_sessions = mgr.find(server_info, cbs, plcy); - if(result.test_is_eq("found some other sessions", found_sessions.size(), size_t(2))) { - for(const auto& [session, handle] : found_sessions) { - result.confirm("ID does not match", !handle.is_id() || handle.id().value() != id); - result.confirm("ticket does not match", - !handle.is_ticket() || handle.ticket().value() != ticket); - } - } + // Session timestamps are saved with second-resolution. If more than + // one session has the same (coarse) timestamp it is undefined which + // will be purged first. The clock tick ensures that session's + // timestamps are unique. + cbs.tick(); + mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), ids[1]); + result.require("first ID is gone", !mgr.retrieve(ids[0], cbs, plcy).has_value()); + result.require("new ID exists", mgr.retrieve(ids[1], cbs, plcy).has_value()); - result.test_is_eq("removing the rest of the sessions", mgr.remove_all(), size_t(2)); - }), + cbs.tick(); + mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), ids[2]); + result.require("second ID is gone", !mgr.retrieve(ids[1], cbs, plcy).has_value()); + result.require("new ID exists", mgr.retrieve(ids[2], cbs, plcy).has_value()); - Botan_Tests::CHECK("old sessions are purged when needed", - [&](auto& result) { - Botan::TLS::Session_Manager_SQLite mgr( - "thetruthisoutthere", Test::rng_as_shared(), Test::temp_file_name("purging.sqlite"), 1); - - std::vector ids = {random_id(), random_id(), random_id()}; - mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), ids[0]); - result.require("new ID exists", mgr.retrieve(ids[0], cbs, plcy).has_value()); - - // Session timestamps are saved with second-resolution. If more than - // one session has the same (coarse) timestamp it is undefined which - // will be purged first. The clock tick ensures that session's - // timestamps are unique. - cbs.tick(); - mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), ids[1]); - result.require("first ID is gone", !mgr.retrieve(ids[0], cbs, plcy).has_value()); - result.require("new ID exists", mgr.retrieve(ids[1], cbs, plcy).has_value()); - - cbs.tick(); - mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), ids[2]); - result.require("second ID is gone", !mgr.retrieve(ids[1], cbs, plcy).has_value()); - result.require("new ID exists", mgr.retrieve(ids[2], cbs, plcy).has_value()); - - result.test_is_eq("only one entry exists", mgr.remove_all(), size_t(1)); - }), + result.test_is_eq("only one entry exists", mgr.remove_all(), size_t(1)); + }), Botan_Tests::CHECK("session purging can be disabled", [&](auto& result) { - Botan::TLS::Session_Manager_SQLite mgr("thetruthisoutthere", - Test::rng_as_shared(), - Test::temp_file_name("purging.sqlite"), - 0 /* no pruning! */); + Botan::TLS::Session_Manager_SQLite mgr( + "thetruthisoutthere", rng, Test::temp_file_name("purging.sqlite"), 0 /* no pruning! */); for(size_t i = 0; i < 25; ++i) { - mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), random_id()); + mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), + random_id(*rng)); } result.test_is_eq("no entries were purged along the way", mgr.remove_all(), size_t(25)); @@ -1003,6 +1008,7 @@ std::vector test_session_manager_sqlite() { } std::vector tls_session_manager_expiry() { + auto rng = Test::new_shared_rng(__func__); Session_Manager_Callbacks cbs; Session_Manager_Policy plcy; @@ -1010,19 +1016,19 @@ std::vector tls_session_manager_expiry() { std::vector()>>> stateful_manager_factories = { {"In Memory", - []() -> std::unique_ptr { - return std::make_unique(Test::rng_as_shared(), 10); + [&rng]() -> std::unique_ptr { + return std::make_unique(rng, 10); }}, {"Stateless", [&]() -> std::unique_ptr { return std::make_unique( - std::make_shared(), Test::rng_as_shared()); + std::make_shared(), rng); }}, #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) {"SQLite", - []() -> std::unique_ptr { + [&rng]() -> std::unique_ptr { return std::make_unique( - "secure_pw", Test::rng_as_shared(), Test::temp_file_name("tls_session_manager_sqlite"), 10); + "secure_pw", rng, Test::temp_file_name("tls_session_manager_sqlite"), 10); }}, #endif }; @@ -1059,12 +1065,12 @@ std::vector tls_session_manager_expiry() { auto mgr = factory(); - auto handle_old = random_id(); + auto handle_old = random_id(*rng); mgr->store(default_session(Botan::TLS::Connection_Side::Client, cbs), handle_old); result.require("session was found", mgr->retrieve(handle_old, cbs, plcy).has_value()); cbs.tick(); - auto handle_new = random_id(); + auto handle_new = random_id(*rng); mgr->store(default_session(Botan::TLS::Connection_Side::Client, cbs), handle_new); result.require("session was found", mgr->retrieve(handle_new, cbs, plcy).has_value()); @@ -1086,18 +1092,18 @@ std::vector tls_session_manager_expiry() { auto mgr = factory(); - auto handle_1 = random_id(); + auto handle_1 = random_id(*rng); mgr->store(default_session(Botan::TLS::Connection_Side::Client, cbs, Botan::TLS::Version_Code::TLS_V12), handle_1); - auto handle_2 = random_ticket(); + auto handle_2 = random_ticket(*rng); mgr->store(default_session(Botan::TLS::Connection_Side::Client, cbs, Botan::TLS::Version_Code::TLS_V12), handle_2); #if defined(BOTAN_HAS_TLS_13) - auto handle_3 = random_id(); + auto handle_3 = random_id(*rng); mgr->store(default_session(Botan::TLS::Connection_Side::Client, cbs, Botan::TLS::Version_Code::TLS_V13), handle_3); - auto handle_4 = random_ticket(); + auto handle_4 = random_ticket(*rng); mgr->store(default_session(Botan::TLS::Connection_Side::Client, cbs, Botan::TLS::Version_Code::TLS_V13), handle_4); #endif @@ -1126,7 +1132,7 @@ std::vector tls_session_manager_expiry() { std::array tickets; for(auto& ticket : tickets) { - ticket = random_ticket(); + ticket = random_ticket(*rng); mgr->store(default_session(Botan::TLS::Connection_Side::Client, cbs), ticket); } diff --git a/src/tests/test_tpm.cpp b/src/tests/test_tpm.cpp index 61c1a1fb111..746ec376de8 100644 --- a/src/tests/test_tpm.cpp +++ b/src/tests/test_tpm.cpp @@ -76,7 +76,7 @@ class UUID_Tests final : public Test { result.test_eq("Uninitialized UUID not valid", empty_uuid.is_valid(), false); - const Botan::UUID random_uuid(Test::rng()); + const Botan::UUID random_uuid(this->rng()); result.test_eq("Random UUID is valid", empty_uuid.is_valid(), false); const Botan::UUID binary_copy(random_uuid.binary_value()); diff --git a/src/tests/test_tss.cpp b/src/tests/test_tss.cpp index 6208b5180e3..c1db2cb217a 100644 --- a/src/tests/test_tss.cpp +++ b/src/tests/test_tss.cpp @@ -46,7 +46,7 @@ class TSS_Recovery_Tests final : public Text_Based_Test { if(N != M) { while(shares.size() > M) { - size_t to_remove = Test::rng().next_byte() % shares.size(); + size_t to_remove = this->rng().next_byte() % shares.size(); shares.erase(shares.begin() + to_remove); try { auto reconstructed_secret = Botan::RTSS_Share::reconstruct(shares); @@ -122,7 +122,7 @@ class TSS_Generation_Tests final : public Text_Based_Test { if(N != M) { while(shares.size() > M) { - size_t to_remove = Test::rng().next_byte() % shares.size(); + size_t to_remove = this->rng().next_byte() % shares.size(); shares.erase(shares.begin() + to_remove); try { diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp index 3ec253df6ec..e5f1b649eda 100644 --- a/src/tests/test_utils.cpp +++ b/src/tests/test_utils.cpp @@ -251,7 +251,7 @@ class CT_Mask_Tests final : public Test { const auto mask = Botan::CT::Mask::expand(static_cast(bad_input)); std::vector input(input_length); - rng().randomize(input.data(), input.size()); + this->rng().randomize(input.data(), input.size()); auto output = Botan::CT::copy_output(mask, input.data(), input.size(), offset); @@ -706,8 +706,8 @@ class UUID_Tests : public Test { Test::Result result("UUID"); const Botan::UUID empty_uuid; - const Botan::UUID random_uuid1(Test::rng()); - const Botan::UUID random_uuid2(Test::rng()); + const Botan::UUID random_uuid1(this->rng()); + const Botan::UUID random_uuid2(this->rng()); const Botan::UUID loaded_uuid(std::vector(16, 4)); result.test_throws("Cannot load wrong number of bytes", []() { Botan::UUID u(std::vector(15)); }); diff --git a/src/tests/test_x509_path.cpp b/src/tests/test_x509_path.cpp index bd7fb5338a0..6310d4ae542 100644 --- a/src/tests/test_x509_path.cpp +++ b/src/tests/test_x509_path.cpp @@ -711,7 +711,8 @@ std::vector BSI_Path_Validation_Tests::run() { * the validation function may be relevant, i.e. if issuer DNs are * ambiguous. */ - struct random_bit_generator { + class random_bit_generator { + public: using result_type = size_t; static constexpr result_type min() { return 0; } @@ -720,10 +721,15 @@ std::vector BSI_Path_Validation_Tests::run() { result_type operator()() { size_t s; - Test::rng().randomize(reinterpret_cast(&s), sizeof(s)); + m_rng.randomize(reinterpret_cast(&s), sizeof(s)); return s; } - } rbg; + + random_bit_generator(Botan::RandomNumberGenerator& rng) : m_rng(rng) {} + + private: + Botan::RandomNumberGenerator& m_rng; + } rbg(this->rng()); for(size_t r = 0; r < 16; r++) { std::shuffle(++(certs.begin()), certs.end(), rbg); diff --git a/src/tests/test_xmss.cpp b/src/tests/test_xmss.cpp index 1dec6f8a61e..a4528c773f1 100644 --- a/src/tests/test_xmss.cpp +++ b/src/tests/test_xmss.cpp @@ -138,16 +138,18 @@ class XMSS_Keygen_Reference_Test final : public Text_Based_Test { }; std::vector xmss_statefulness() { - auto sign_something = [](auto& sk) { + auto rng = Test::new_rng(__func__); + + auto sign_something = [&rng](auto& sk) { auto msg = Botan::hex_decode("deadbeef"); - Botan::PK_Signer signer(sk, Test::rng(), "SHA2_10_256"); - signer.sign_message(msg, Test::rng()); + Botan::PK_Signer signer(sk, *rng, "SHA2_10_256"); + signer.sign_message(msg, *rng); }; return {CHECK("signing alters state", [&](auto& result) { - Botan::XMSS_PrivateKey sk(Botan::XMSS_Parameters::XMSS_SHA2_10_256, Test::rng()); + Botan::XMSS_PrivateKey sk(Botan::XMSS_Parameters::XMSS_SHA2_10_256, *rng); result.require("allows 1024 signatures", sk.remaining_operations() == 1024); sign_something(sk); @@ -265,11 +267,13 @@ std::vector xmss_legacy_private_key() { const auto message = Botan::hex_decode("deadcafe"); const auto algo_name = "SHA2_10_256"; + auto rng = Test::new_rng(__func__); + return { Botan_Tests::CHECK("Use a legacy private key to create a signature", [&](auto& result) { - Botan::PK_Signer signer(legacy_secret_key, Test::rng(), algo_name); - auto signature = signer.sign_message(message, Test::rng()); + Botan::PK_Signer signer(legacy_secret_key, *rng, algo_name); + auto signature = signer.sign_message(message, *rng); Botan::PK_Verifier verifier(public_key_from_secret_key, algo_name); result.confirm("legacy private key generates signatures that are still verifiable", @@ -285,8 +289,8 @@ std::vector xmss_legacy_private_key() { Botan_Tests::CHECK("Verify a new signature by a legacy private key with a legacy public key", [&](auto& result) { - Botan::PK_Signer signer(legacy_secret_key, Test::rng(), algo_name); - auto signature = signer.sign_message(message, Test::rng()); + Botan::PK_Signer signer(legacy_secret_key, *rng, algo_name); + auto signature = signer.sign_message(message, *rng); Botan::PK_Verifier verifier(legacy_public_key, algo_name); result.confirm("legacy private key generates signatures that are still verifiable", diff --git a/src/tests/test_zfec.cpp b/src/tests/test_zfec.cpp index 199cf66c962..736da4fe652 100644 --- a/src/tests/test_zfec.cpp +++ b/src/tests/test_zfec.cpp @@ -90,7 +90,7 @@ class ZFEC_KAT final : public Text_Based_Test { shares_decoded.clear(); while(shares.size() != K) { - const size_t idx = rng().next_byte(); + const size_t idx = this->rng().next_byte(); shares.erase(idx); } diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index bab502a8278..c6ce80b7884 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -389,6 +389,19 @@ bool Test::Result::test_rc(const std::string& func, int expected, int rc) { return test_success(); } +void Test::initialize(std::string test_name, CodeLocation location) { + m_test_name = std::move(test_name); + m_registration_location = std::move(location); +} + +Botan::RandomNumberGenerator& Test::rng() const { + if(!m_test_rng) { + m_test_rng = Test::new_rng(m_test_name); + } + + return *m_test_rng; +} + std::vector Test::possible_providers(const std::string& /*unused*/) { return Test::provider_filter({"base"}); } @@ -694,16 +707,76 @@ std::vector Test::read_binary_data_file(const std::string& path) { // NOLINTNEXTLINE(*-avoid-non-const-global-variables) Test_Options Test::m_opts; // NOLINTNEXTLINE(*-avoid-non-const-global-variables) -std::shared_ptr Test::m_test_rng; +std::string Test::m_test_rng_seed; //static void Test::set_test_options(const Test_Options& opts) { m_opts = opts; } +namespace { + +/* +* This is a fast, simple, deterministic PRNG that's used for running +* the tests. It is not intended to be cryptographically secure. +*/ +class Testsuite_RNG final : public Botan::RandomNumberGenerator { + public: + std::string name() const override { return "Testsuite_RNG"; } + + void clear() override { m_x = 0; } + + bool accepts_input() const override { return true; } + + bool is_seeded() const override { return true; } + + void fill_bytes_with_input(std::span output, std::span input) override { + for(const auto byte : input) { + mix(byte); + } + + for(auto& byte : output) { + byte = mix(); + } + } + + Testsuite_RNG(std::string_view seed, std::string_view test_name) { + m_x = 0; + + for(char c : seed) { + this->mix(static_cast(c)); + } + for(char c : test_name) { + this->mix(static_cast(c)); + } + } + + private: + uint8_t mix(uint8_t input = 0) { + m_x ^= input; + m_x *= 0xF2E16957; + m_x += 0xE50B590F; + return static_cast(m_x >> 27); + } + + uint64_t m_x; +}; + +} // namespace + +//static +void Test::set_test_rng_seed(std::span seed, size_t epoch) { + m_test_rng_seed = Botan::fmt("seed={} epoch={}", Botan::hex_encode(seed), epoch); +} + //static -void Test::set_test_rng(std::shared_ptr rng) { - m_test_rng = std::move(rng); +std::unique_ptr Test::new_rng(std::string_view test_name) { + return std::make_unique(m_test_rng_seed, test_name); +} + +//static +std::shared_ptr Test::new_shared_rng(std::string_view test_name) { + return std::make_shared(m_test_rng_seed, test_name); } //static @@ -738,25 +811,9 @@ std::vector Test::provider_filter(const std::vector& i return std::vector{}; } -//static -Botan::RandomNumberGenerator& Test::rng() { - if(!m_test_rng) { - throw Test_Error("Test requires RNG but no RNG set with Test::set_test_rng"); - } - return *m_test_rng; -} - -//static -std::shared_ptr Test::rng_as_shared() { - if(!m_test_rng) { - throw Test_Error("Test requires RNG but no RNG set with Test::set_test_rng"); - } - return m_test_rng; -} - -std::string Test::random_password() { - const size_t len = 1 + Test::rng().next_byte() % 32; - return Botan::hex_encode(Test::rng().random_vec(len)); +std::string Test::random_password(Botan::RandomNumberGenerator& rng) { + const size_t len = 1 + rng.next_byte() % 32; + return Botan::hex_encode(rng.random_vec(len)); } std::vector> VarMap::get_req_bin_list(const std::string& key) const { diff --git a/src/tests/tests.h b/src/tests/tests.h index ea7c0e5c7c0..70776e20160 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -563,11 +563,11 @@ class Test { virtual std::vector possible_providers(const std::string&); - void set_test_name(const std::string& name) { m_test_name = name; } + void initialize(std::string test_name, CodeLocation location); const std::string& test_name() const { return m_test_name; } - void set_registration_location(CodeLocation location) { m_registration_location = std::move(location); } + Botan::RandomNumberGenerator& rng() const; const std::optional& registration_location() const { return m_registration_location; } @@ -597,10 +597,9 @@ class Test { template static std::vector mutate_vec(const std::vector& v, + Botan::RandomNumberGenerator& rng, bool maybe_resize = false, size_t min_offset = 0) { - auto& rng = Test::rng(); - std::vector r = v; if(maybe_resize && (r.empty() || rng.next_byte() < 32)) { @@ -621,7 +620,7 @@ class Test { static void set_test_options(const Test_Options& opts); - static void set_test_rng(std::shared_ptr rng); + static void set_test_rng_seed(std::span seed, size_t epoch = 0); static const Test_Options& options() { return m_opts; } @@ -641,19 +640,24 @@ class Test { static std::string read_data_file(const std::string& path); static std::vector read_binary_data_file(const std::string& path); - static Botan::RandomNumberGenerator& rng(); - static std::shared_ptr rng_as_shared(); - static std::string random_password(); + static std::unique_ptr new_rng(std::string_view test_name); + static std::shared_ptr new_shared_rng(std::string_view test_name); + + static std::string random_password(Botan::RandomNumberGenerator& rng); static uint64_t timestamp(); // nanoseconds arbitrary epoch static std::vector flatten_result_lists(std::vector> result_lists); private: static Test_Options m_opts; - static std::shared_ptr m_test_rng; - - std::string m_test_name; // The string ID that was used to register this test - std::optional m_registration_location; /// The source file location where the test was registered + static std::string m_test_rng_seed; + + /// The string ID that was used to register this test + std::string m_test_name; + /// The source file location where the test was registered + std::optional m_registration_location; + /// The test-specific RNG state + mutable std::unique_ptr m_test_rng; }; /* @@ -669,8 +673,7 @@ class TestClassRegistration { CodeLocation registration_location) { Test::register_test(category, name, smoke_test, needs_serialization, [=] { auto test = std::make_unique(); - test->set_test_name(name); - test->set_registration_location(registration_location); + test->initialize(name, registration_location); return test; }); } @@ -747,8 +750,7 @@ class TestFnRegistration { TestFns... fn) { Test::register_test(category, name, smoke_test, needs_serialization, [=] { auto test = std::make_unique(fn...); - test->set_test_name(name); - test->set_registration_location(std::move(registration_location)); + test->initialize(name, registration_location); return test; }); } diff --git a/src/tests/unit_ecdh.cpp b/src/tests/unit_ecdh.cpp index 6629aafce18..2c773fb56af 100644 --- a/src/tests/unit_ecdh.cpp +++ b/src/tests/unit_ecdh.cpp @@ -24,13 +24,13 @@ class ECDH_Unit_Tests final : public Test { std::vector run() override { std::vector results; - results.push_back(test_ecdh_normal_derivation()); + results.push_back(test_ecdh_normal_derivation(this->rng())); return results; } private: - static Test::Result test_ecdh_normal_derivation() { + static Test::Result test_ecdh_normal_derivation(Botan::RandomNumberGenerator& rng) { Test::Result result("ECDH key exchange"); std::vector params = {"secp256r1", "secp384r1", "secp521r1", "brainpool256r1"}; @@ -38,11 +38,11 @@ class ECDH_Unit_Tests final : public Test { for(const auto& param : params) { try { Botan::EC_Group dom_pars(param); - Botan::ECDH_PrivateKey private_a(Test::rng(), dom_pars); - Botan::ECDH_PrivateKey private_b(Test::rng(), dom_pars); + Botan::ECDH_PrivateKey private_a(rng, dom_pars); + Botan::ECDH_PrivateKey private_b(rng, dom_pars); - Botan::PK_Key_Agreement ka(private_a, Test::rng(), "KDF2(SHA-512)"); - Botan::PK_Key_Agreement kb(private_b, Test::rng(), "KDF2(SHA-512)"); + Botan::PK_Key_Agreement ka(private_a, rng, "KDF2(SHA-512)"); + Botan::PK_Key_Agreement kb(private_b, rng, "KDF2(SHA-512)"); Botan::SymmetricKey alice_key = ka.derive_key(32, private_b.public_value()); Botan::SymmetricKey bob_key = kb.derive_key(32, private_a.public_value()); diff --git a/src/tests/unit_ecdsa.cpp b/src/tests/unit_ecdsa.cpp index aefdf77c986..7b66afafa43 100644 --- a/src/tests/unit_ecdsa.cpp +++ b/src/tests/unit_ecdsa.cpp @@ -44,7 +44,9 @@ Test::Result test_hash_larger_than_n() { // n = 0x0100000000000000000001f4c8f927aed3ca752257 (21 bytes) - Botan::ECDSA_PrivateKey priv_key(Test::rng(), dom_pars); + auto rng = Test::new_rng("ecdsa_hash_larger_than_n"); + + Botan::ECDSA_PrivateKey priv_key(*rng, dom_pars); std::vector message(20); std::iota(message.begin(), message.end(), static_cast(0)); @@ -57,16 +59,16 @@ Test::Result test_hash_larger_than_n() { return result; } - Botan::PK_Signer pk_signer_160(priv_key, Test::rng(), "SHA-1"); + Botan::PK_Signer pk_signer_160(priv_key, *rng, "SHA-1"); Botan::PK_Verifier pk_verifier_160(priv_key, "SHA-1"); // Verify we can sign and verify with SHA-1 - std::vector signature_160 = pk_signer_160.sign_message(message, Test::rng()); + std::vector signature_160 = pk_signer_160.sign_message(message, *rng); result.test_eq("message verifies", pk_verifier_160.verify_message(message, signature_160), true); // Verify we can sign and verify with SHA-224 - Botan::PK_Signer pk_signer(priv_key, Test::rng(), "SHA-224"); - std::vector signature = pk_signer.sign_message(message, Test::rng()); + Botan::PK_Signer pk_signer(priv_key, *rng, "SHA-224"); + std::vector signature = pk_signer.sign_message(message, *rng); Botan::PK_Verifier pk_verifier(priv_key, "SHA-224"); result.test_eq("message verifies", pk_verifier.verify_message(message, signature), true); @@ -124,19 +126,22 @@ Test::Result test_decode_ver_link_SHA1() { Test::Result test_sign_then_ver() { Test::Result result("ECDSA Unit"); + auto rng = Test::new_rng("ecdsa_sign_then_verify"); + Botan::EC_Group dom_pars("secp160r1"); - Botan::ECDSA_PrivateKey ecdsa(Test::rng(), dom_pars); + Botan::ECDSA_PrivateKey ecdsa(*rng, dom_pars); - Botan::PK_Signer signer(ecdsa, Test::rng(), "SHA-256"); + Botan::PK_Signer signer(ecdsa, *rng, "SHA-256"); auto msg = Botan::hex_decode("12345678901234567890abcdef12"); - std::vector sig = signer.sign_message(msg, Test::rng()); + std::vector sig = signer.sign_message(msg, *rng); Botan::PK_Verifier verifier(ecdsa, "SHA-256"); result.confirm("signature verifies", verifier.verify_message(msg, sig)); - result.confirm("invalid signature rejected", !verifier.verify_message(msg, Test::mutate_vec(sig))); + const bool accept = verifier.verify_message(msg, Test::mutate_vec(sig, *rng)); + result.confirm("invalid signature rejected", !accept); return result; } @@ -144,16 +149,18 @@ Test::Result test_sign_then_ver() { Test::Result test_ec_sign() { Test::Result result("ECDSA Unit"); + auto rng = Test::new_rng("ecdsa_sign"); + try { Botan::EC_Group dom_pars("secp160r1"); - Botan::ECDSA_PrivateKey priv_key(Test::rng(), dom_pars); - Botan::PK_Signer signer(priv_key, Test::rng(), "SHA-224"); + Botan::ECDSA_PrivateKey priv_key(*rng, dom_pars); + Botan::PK_Signer signer(priv_key, *rng, "SHA-224"); Botan::PK_Verifier verifier(priv_key, "SHA-224"); for(size_t i = 0; i != 256; ++i) { signer.update(static_cast(i)); } - std::vector sig = signer.signature(Test::rng()); + std::vector sig = signer.signature(*rng); for(size_t i = 0; i != 256; ++i) { verifier.update(static_cast(i)); @@ -191,12 +198,14 @@ Test::Result test_ecdsa_create_save_load() { const std::vector msg = Botan::hex_decode("12345678901234567890abcdef12"); std::vector msg_signature; + auto rng = Test::new_rng("ecdsa_save_and_load"); + try { Botan::EC_Group dom_pars("secp160r1"); - Botan::ECDSA_PrivateKey key(Test::rng(), dom_pars); + Botan::ECDSA_PrivateKey key(*rng, dom_pars); - Botan::PK_Signer signer(key, Test::rng(), "SHA-256"); - msg_signature = signer.sign_message(msg, Test::rng()); + Botan::PK_Signer signer(key, *rng, "SHA-256"); + msg_signature = signer.sign_message(msg, *rng); ecc_private_key_pem = Botan::PKCS8::PEM_encode(key); } catch(std::exception& e) { @@ -221,6 +230,8 @@ Test::Result test_ecdsa_create_save_load() { Test::Result test_unusual_curve() { Test::Result result("ECDSA Unit"); + auto rng = Test::new_rng("ecdsa_unusual_curve"); + //calc a curve which is not in the registry const Botan::BigInt p( "2117607112719756483104013348936480976596328609518055062007450442679169492999007105354629105748524349829824407773719892437896937279095106809"); @@ -244,7 +255,7 @@ Test::Result test_unusual_curve() { return result; } - Botan::ECDSA_PrivateKey key_odd_curve(Test::rng(), dom_params); + Botan::ECDSA_PrivateKey key_odd_curve(*rng, dom_params); std::string key_odd_curve_str = Botan::PKCS8::PEM_encode(key_odd_curve); Botan::DataSource_Memory key_data_src(key_odd_curve_str); @@ -258,8 +269,10 @@ Test::Result test_unusual_curve() { Test::Result test_encoding_options() { Test::Result result("ECDSA Unit"); + auto rng = Test::new_rng("ecdsa_encoding_options"); + Botan::EC_Group group("secp256r1"); - Botan::ECDSA_PrivateKey key(Test::rng(), group); + Botan::ECDSA_PrivateKey key(*rng, group); result.confirm("Default encoding is uncompressed", key.point_encoding() == Botan::EC_Point_Format::Uncompressed); @@ -301,6 +314,8 @@ Test::Result test_encoding_options() { Test::Result test_read_pkcs8() { Test::Result result("ECDSA Unit"); + auto rng = Test::new_rng("ecdsa_read_pkcs8"); + const std::vector msg = Botan::hex_decode("12345678901234567890abcdef12"); try { @@ -314,10 +329,10 @@ Test::Result test_read_pkcs8() { result.confirm("EC_Group is marked as explicit encoding", ecdsa_nodp->domain().used_explicit_encoding()); - Botan::PK_Signer signer(*ecdsa_nodp, Test::rng(), "SHA-256"); + Botan::PK_Signer signer(*ecdsa_nodp, *rng, "SHA-256"); Botan::PK_Verifier verifier(*ecdsa_nodp, "SHA-256"); - std::vector signature_nodp = signer.sign_message(msg, Test::rng()); + std::vector signature_nodp = signer.sign_message(msg, *rng); result.confirm("signature valid", verifier.verify_message(msg, signature_nodp)); @@ -374,16 +389,18 @@ Test::Result test_ecc_key_with_rfc5915_parameters() { Test::Result test_curve_registry() { Test::Result result("ECDSA Unit"); + auto rng = Test::new_rng("curve_registry"); + for(const std::string& group_name : Botan::EC_Group::known_named_groups()) { try { Botan::EC_Group group(group_name); - Botan::ECDSA_PrivateKey ecdsa(Test::rng(), group); + Botan::ECDSA_PrivateKey ecdsa(*rng, group); - Botan::PK_Signer signer(ecdsa, Test::rng(), "SHA-256"); + Botan::PK_Signer signer(ecdsa, *rng, "SHA-256"); Botan::PK_Verifier verifier(ecdsa, "SHA-256"); const std::vector msg = Botan::hex_decode("12345678901234567890abcdef12"); - const std::vector sig = signer.sign_message(msg, Test::rng()); + const std::vector sig = signer.sign_message(msg, *rng); result.confirm("verified signature", verifier.verify_message(msg, sig)); } catch(Botan::Invalid_Argument& e) { diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index 78aabbbd43a..be0b602506f 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -493,19 +493,17 @@ class TLS_Handshake_Test final { void TLS_Handshake_Test::go() { m_results.start_timer(); - Botan::RandomNumberGenerator& rng = Test::rng(); - const std::vector protocols_offered = {"test/1", "test/2"}; // Choose random application data to send - const size_t c_len = 1 + ((static_cast(rng.next_byte()) << 4) ^ rng.next_byte()); + const size_t c_len = 1 + ((static_cast(m_rng->next_byte()) << 4) ^ m_rng->next_byte()); std::vector client_msg(c_len); - Test::rng().randomize(client_msg.data(), client_msg.size()); + m_rng->randomize(client_msg.data(), client_msg.size()); bool client_has_written = false; - const size_t s_len = 1 + ((static_cast(rng.next_byte()) << 4) ^ rng.next_byte()); + const size_t s_len = 1 + ((static_cast(m_rng->next_byte()) << 4) ^ m_rng->next_byte()); std::vector server_msg(s_len); - Test::rng().randomize(server_msg.data(), server_msg.size()); + m_rng->randomize(server_msg.data(), server_msg.size()); bool server_has_written = false; std::unique_ptr client; @@ -545,7 +543,7 @@ void TLS_Handshake_Test::go() { size_t sent_so_far = 0; while(sent_so_far != client_msg.size()) { const size_t left = client_msg.size() - sent_so_far; - const size_t rnd12 = (rng.next_byte() << 4) ^ rng.next_byte(); + const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte(); const size_t sending = std::min(left, rnd12); client->send(&client_msg[sent_so_far], sending); @@ -561,7 +559,7 @@ void TLS_Handshake_Test::go() { size_t sent_so_far = 0; while(sent_so_far != server_msg.size()) { const size_t left = server_msg.size() - sent_so_far; - const size_t rnd12 = (rng.next_byte() << 4) ^ rng.next_byte(); + const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte(); const size_t sending = std::min(left, rnd12); m_server->send(&server_msg[sent_so_far], sending); @@ -676,9 +674,8 @@ class TLS_Unit_Tests final : public Test { const std::shared_ptr& creds, const std::vector& versions, const std::shared_ptr& policy, + std::shared_ptr& rng, bool client_auth = false) { - auto rng = Test::rng_as_shared(); - try { for(const auto& version : versions) { TLS_Handshake_Test test(version.to_string() + " " + test_descr, @@ -715,6 +712,7 @@ class TLS_Unit_Tests final : public Test { const std::shared_ptr& client_ses, const std::shared_ptr& server_ses, const std::shared_ptr& creds, + std::shared_ptr& rng, const std::string& kex_policy, const std::string& cipher_policy, const std::string& mac_policy, @@ -736,7 +734,8 @@ class TLS_Unit_Tests final : public Test { std::vector versions = {Botan::TLS::Protocol_Version::TLS_V12, Botan::TLS::Protocol_Version::DTLS_V12}; - return test_with_policy(test_descr, results, client_ses, server_ses, creds, versions, policy, client_auth); + return test_with_policy( + test_descr, results, client_ses, server_ses, creds, versions, policy, rng, client_auth); } static void test_modern_versions(const std::string& test_descr, @@ -744,6 +743,7 @@ class TLS_Unit_Tests final : public Test { const std::shared_ptr& client_ses, const std::shared_ptr& server_ses, const std::shared_ptr& creds, + std::shared_ptr& rng, const std::string& kex_policy, const std::string& cipher_policy, const std::string& mac_policy = "AEAD", @@ -754,6 +754,7 @@ class TLS_Unit_Tests final : public Test { client_ses, server_ses, creds, + rng, kex_policy, cipher_policy, mac_policy, @@ -766,6 +767,7 @@ class TLS_Unit_Tests final : public Test { const std::shared_ptr& client_ses, const std::shared_ptr& server_ses, const std::shared_ptr& creds, + std::shared_ptr& rng, const std::string& kex_policy, const std::string& cipher_policy, const std::string& mac_policy, @@ -789,7 +791,8 @@ class TLS_Unit_Tests final : public Test { std::vector versions = {Botan::TLS::Protocol_Version::TLS_V12, Botan::TLS::Protocol_Version::DTLS_V12}; - return test_with_policy(test_descr, results, client_ses, server_ses, creds, versions, policy, client_auth); + return test_with_policy( + test_descr, results, client_ses, server_ses, creds, versions, policy, rng, client_auth); } void test_session_established_abort(std::vector& results, @@ -865,7 +868,7 @@ class TLS_Unit_Tests final : public Test { std::vector run() override { std::vector results; - auto rng = Test::rng_as_shared(); + auto rng = Test::new_shared_rng(this->test_name()); std::shared_ptr client_ses; std::shared_ptr server_ses; @@ -883,22 +886,39 @@ class TLS_Unit_Tests final : public Test { #if defined(BOTAN_HAS_TLS_CBC) for(std::string etm_setting : {"false", "true"}) { - test_all_versions( - "AES-128 RSA", results, client_ses, server_ses, creds, "RSA", "AES-128", "SHA-256 SHA-1", etm_setting); - test_all_versions( - "AES-128 ECDH", results, client_ses, server_ses, creds, "ECDH", "AES-128", "SHA-256 SHA-1", etm_setting); + test_all_versions("AES-128 RSA", + results, + client_ses, + server_ses, + creds, + rng, + "RSA", + "AES-128", + "SHA-256 SHA-1", + etm_setting); + test_all_versions("AES-128 ECDH", + results, + client_ses, + server_ses, + creds, + rng, + "ECDH", + "AES-128", + "SHA-256 SHA-1", + etm_setting); #if defined(BOTAN_HAS_DES) - test_all_versions("3DES RSA", results, client_ses, server_ses, creds, "RSA", "3DES", "SHA-1", etm_setting); test_all_versions( - "3DES ECDH", results, client_ses, server_ses, creds, "ECDH", "3DES", "SHA-1", etm_setting); + "3DES RSA", results, client_ses, server_ses, creds, rng, "RSA", "3DES", "SHA-1", etm_setting); + test_all_versions( + "3DES ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "3DES", "SHA-1", etm_setting); #endif server_ses->remove_all(); } client_ses->remove_all(); - test_modern_versions("AES-128 DH", results, client_ses, server_ses, creds, "DH", "AES-128", "SHA-256"); + test_modern_versions("AES-128 DH", results, client_ses, server_ses, creds, rng, "DH", "AES-128", "SHA-256"); #endif @@ -909,23 +929,31 @@ class TLS_Unit_Tests final : public Test { server_ses, creds, {Botan::TLS::Protocol_Version::TLS_V12}, - strict_policy); + strict_policy, + rng); auto suiteb_128 = std::make_shared(); - test_with_policy( - "Suite B", results, client_ses, server_ses, creds, {Botan::TLS::Protocol_Version::TLS_V12}, suiteb_128); + test_with_policy("Suite B", + results, + client_ses, + server_ses, + creds, + {Botan::TLS::Protocol_Version::TLS_V12}, + suiteb_128, + rng); // Remove server sessions before client, so clients retry with session server doesn't know server_ses->remove_all(); - test_modern_versions("AES-128/GCM RSA", results, client_ses, server_ses, creds, "RSA", "AES-128/GCM"); - test_modern_versions("AES-128/GCM ECDH", results, client_ses, server_ses, creds, "ECDH", "AES-128/GCM"); + test_modern_versions("AES-128/GCM RSA", results, client_ses, server_ses, creds, rng, "RSA", "AES-128/GCM"); + test_modern_versions("AES-128/GCM ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "AES-128/GCM"); test_modern_versions("AES-128/GCM ECDH RSA", results, client_ses, server_ses, creds, + rng, "ECDH", "AES-128/GCM", "AEAD", @@ -936,6 +964,7 @@ class TLS_Unit_Tests final : public Test { client_ses, server_ses, creds, + rng, "ECDH", "AES-128/GCM", "AEAD", @@ -945,11 +974,12 @@ class TLS_Unit_Tests final : public Test { #if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM) test_modern_versions( - "Camellia-128/GCM ECDH", results, client_ses, server_ses, creds, "ECDH", "Camellia-128/GCM", "AEAD"); + "Camellia-128/GCM ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "Camellia-128/GCM", "AEAD"); #endif #if defined(BOTAN_HAS_ARIA) - test_modern_versions("ARIA/GCM ECDH", results, client_ses, server_ses, creds, "ECDH", "ARIA-128/GCM", "AEAD"); + test_modern_versions( + "ARIA/GCM ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "ARIA-128/GCM", "AEAD"); #endif test_modern_versions("AES-128/GCM point compression", @@ -957,6 +987,7 @@ class TLS_Unit_Tests final : public Test { client_ses, server_ses, creds, + rng, "ECDH", "AES-128/GCM", "AEAD", @@ -966,6 +997,7 @@ class TLS_Unit_Tests final : public Test { client_ses, server_ses, creds, + rng, "ECDH", "AES-256/GCM", "AEAD", @@ -975,6 +1007,7 @@ class TLS_Unit_Tests final : public Test { client_ses, server_ses, creds, + rng, "ECDH", "AES-128/GCM", "AEAD", @@ -982,7 +1015,7 @@ class TLS_Unit_Tests final : public Test { #if defined(BOTAN_HAS_CURVE_25519) test_modern_versions( - "AES-128/GCM x25519", results, client_ses, server_ses, creds, "ECDH", "AES-128/GCM", "AEAD", {{ + "AES-128/GCM x25519", results, client_ses, server_ses, creds, rng, "ECDH", "AES-128/GCM", "AEAD", {{ "groups", "x25519" }}); @@ -993,6 +1026,7 @@ class TLS_Unit_Tests final : public Test { client_ses, server_ses, creds, + rng, "DH", "AES-128/GCM", "AEAD", @@ -1006,6 +1040,7 @@ class TLS_Unit_Tests final : public Test { client_ses, server_ses, creds_with_client_cert, + rng, "ECDH", "AES-256/GCM", "AEAD", @@ -1017,25 +1052,27 @@ class TLS_Unit_Tests final : public Test { #endif #if defined(BOTAN_HAS_AEAD_OCB) - test_modern_versions("AES-256/OCB ECDH", results, client_ses, server_ses, creds, "ECDH", "AES-256/OCB(12)"); + test_modern_versions( + "AES-256/OCB ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "AES-256/OCB(12)"); #endif server_ses->remove_all(); #if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) test_modern_versions( - "ChaCha20Poly1305 ECDH", results, client_ses, server_ses, creds, "ECDH", "ChaCha20Poly1305"); + "ChaCha20Poly1305 ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "ChaCha20Poly1305"); #endif - test_modern_versions("AES-128/GCM PSK", results, client_ses, server_ses, creds, "PSK", "AES-128/GCM"); + test_modern_versions("AES-128/GCM PSK", results, client_ses, server_ses, creds, rng, "PSK", "AES-128/GCM"); #if defined(BOTAN_HAS_AEAD_CCM) - test_modern_versions("AES-128/CCM PSK", results, client_ses, server_ses, creds, "PSK", "AES-128/CCM"); - test_modern_versions("AES-128/CCM-8 PSK", results, client_ses, server_ses, creds, "PSK", "AES-128/CCM(8)"); + test_modern_versions("AES-128/CCM PSK", results, client_ses, server_ses, creds, rng, "PSK", "AES-128/CCM"); + test_modern_versions( + "AES-128/CCM-8 PSK", results, client_ses, server_ses, creds, rng, "PSK", "AES-128/CCM(8)"); #endif test_modern_versions( - "AES-128/GCM ECDHE_PSK", results, client_ses, server_ses, creds, "ECDHE_PSK", "AES-128/GCM"); + "AES-128/GCM ECDHE_PSK", results, client_ses, server_ses, creds, rng, "ECDHE_PSK", "AES-128/GCM"); // Test with a custom curve @@ -1061,6 +1098,7 @@ class TLS_Unit_Tests final : public Test { client_ses, server_ses, creds, + rng, "ECDH", "AES-256/GCM", "AEAD", @@ -1147,15 +1185,17 @@ class DTLS_Reconnection_Test : public Test { Test::Result result("DTLS reconnection"); + auto rng = Test::new_shared_rng(this->test_name()); + auto server_policy = std::make_shared(); auto client_policy = std::make_shared(); auto creds = std::make_shared(); - auto server_sessions = std::make_shared(rng_as_shared()); + auto server_sessions = std::make_shared(rng); auto client_sessions = std::make_shared(); std::vector s2c, server_recv; auto server_callbacks = std::make_shared(result, s2c, server_recv); - Botan::TLS::Server server(server_callbacks, server_sessions, creds, server_policy, rng_as_shared(), true); + Botan::TLS::Server server(server_callbacks, server_sessions, creds, server_policy, rng, true); std::vector c1_c2s, client1_recv; auto client1_callbacks = std::make_shared(result, c1_c2s, client1_recv); @@ -1163,7 +1203,7 @@ class DTLS_Reconnection_Test : public Test { client_sessions, creds, client_policy, - rng_as_shared(), + rng, Botan::TLS::Server_Information("localhost"), Botan::TLS::Protocol_Version::latest_dtls_version()); @@ -1225,7 +1265,7 @@ class DTLS_Reconnection_Test : public Test { client_sessions, creds, client_policy, - rng_as_shared(), + rng, Botan::TLS::Server_Information("localhost"), Botan::TLS::Protocol_Version::latest_dtls_version()); diff --git a/src/tests/unit_tls_policy.cpp b/src/tests/unit_tls_policy.cpp index e11c45309e9..efd33da84d7 100644 --- a/src/tests/unit_tls_policy.cpp +++ b/src/tests/unit_tls_policy.cpp @@ -40,9 +40,9 @@ class TLS_Policy_Unit_Tests final : public Test { std::vector run() override { std::vector results; - results.push_back(test_peer_key_acceptable_rsa()); - results.push_back(test_peer_key_acceptable_ecdh()); - results.push_back(test_peer_key_acceptable_ecdsa()); + results.push_back(test_peer_key_acceptable_rsa(this->rng())); + results.push_back(test_peer_key_acceptable_ecdh(this->rng())); + results.push_back(test_peer_key_acceptable_ecdsa(this->rng())); results.push_back(test_peer_key_acceptable_dh()); results.push_back(test_key_exchange_groups_to_offer()); @@ -50,10 +50,10 @@ class TLS_Policy_Unit_Tests final : public Test { } private: - static Test::Result test_peer_key_acceptable_rsa() { + static Test::Result test_peer_key_acceptable_rsa(Botan::RandomNumberGenerator& rng) { Test::Result result("TLS Policy RSA key verification"); #if defined(BOTAN_HAS_RSA) - auto rsa_key_1024 = std::make_unique(Test::rng(), 1024); + auto rsa_key_1024 = std::make_unique(rng, 1024); Botan::TLS::Policy policy; try { @@ -63,18 +63,18 @@ class TLS_Policy_Unit_Tests final : public Test { result.test_success("Correctly rejecting 1024 bit RSA keys"); } - auto rsa_key_2048 = std::make_unique(Test::rng(), 2048); + auto rsa_key_2048 = std::make_unique(rng, 2048); policy.check_peer_key_acceptable(*rsa_key_2048); result.test_success("Correctly accepting 2048 bit RSA keys"); #endif return result; } - static Test::Result test_peer_key_acceptable_ecdh() { + static Test::Result test_peer_key_acceptable_ecdh(Botan::RandomNumberGenerator& rng) { Test::Result result("TLS Policy ECDH key verification"); #if defined(BOTAN_HAS_ECDH) Botan::EC_Group group_192("secp192r1"); - auto ecdh_192 = std::make_unique(Test::rng(), group_192); + auto ecdh_192 = std::make_unique(rng, group_192); Botan::TLS::Policy policy; try { @@ -85,18 +85,18 @@ class TLS_Policy_Unit_Tests final : public Test { } Botan::EC_Group group_256("secp256r1"); - auto ecdh_256 = std::make_unique(Test::rng(), group_256); + auto ecdh_256 = std::make_unique(rng, group_256); policy.check_peer_key_acceptable(*ecdh_256); result.test_success("Correctly accepting 256 bit EC keys"); #endif return result; } - static Test::Result test_peer_key_acceptable_ecdsa() { + static Test::Result test_peer_key_acceptable_ecdsa(Botan::RandomNumberGenerator& rng) { Test::Result result("TLS Policy ECDSA key verification"); #if defined(BOTAN_HAS_ECDSA) Botan::EC_Group group_192("secp192r1"); - auto ecdsa_192 = std::make_unique(Test::rng(), group_192); + auto ecdsa_192 = std::make_unique(rng, group_192); Botan::TLS::Policy policy; try { @@ -107,7 +107,7 @@ class TLS_Policy_Unit_Tests final : public Test { } Botan::EC_Group group_256("secp256r1"); - auto ecdsa_256 = std::make_unique(Test::rng(), group_256); + auto ecdsa_256 = std::make_unique(rng, group_256); policy.check_peer_key_acceptable(*ecdsa_256); result.test_success("Correctly accepting 256 bit EC keys"); #endif diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp index f044966fa4e..d16b3d01395 100644 --- a/src/tests/unit_x509.cpp +++ b/src/tests/unit_x509.cpp @@ -97,7 +97,7 @@ Botan::X509_Cert_Options req_opts3(const std::string& sig_padding = "") { return opts; } -std::unique_ptr make_a_private_key(const std::string& algo) { +std::unique_ptr make_a_private_key(const std::string& algo, Botan::RandomNumberGenerator& rng) { const std::string params = [&] { // Here we override defaults as needed if(algo == "RSA") { @@ -112,7 +112,7 @@ std::unique_ptr make_a_private_key(const std::string& algo) return ""; // default "" means choose acceptable algo-specific params }(); - return Botan::create_private_key(algo, Test::rng(), params); + return Botan::create_private_key(algo, rng, params); } Test::Result test_cert_status_strings() { @@ -407,15 +407,17 @@ Test::Result test_crl_dn_name() { // See GH #1252 #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1) + auto rng = Test::new_rng(__func__); + const Botan::OID dc_oid("0.9.2342.19200300.100.1.25"); Botan::X509_Certificate cert(Test::data_file("x509/misc/opcuactt_ca.der")); Botan::DataSource_Stream key_input(Test::data_file("x509/misc/opcuactt_ca.pem")); auto key = Botan::PKCS8::load_key(key_input); - Botan::X509_CA ca(cert, *key, "SHA-256", Test::rng()); + Botan::X509_CA ca(cert, *key, "SHA-256", *rng); - Botan::X509_CRL crl = ca.new_crl(Test::rng()); + Botan::X509_CRL crl = ca.new_crl(*rng); result.confirm("matches issuer cert", crl.issuer_dn() == cert.subject_dn()); @@ -594,6 +596,8 @@ Test::Result test_x509_authority_info_access_extension() { Test::Result test_x509_encode_authority_info_access_extension() { Test::Result result("X509 with encoded PKIX.AuthorityInformationAccess extension"); + auto rng = Test::new_rng(__func__); + const std::string sig_algo{"RSA"}; const std::string hash_fn{"SHA-256"}; const std::string padding_method{"EMSA3(SHA-256)"}; @@ -607,19 +611,19 @@ Test::Result test_x509_encode_authority_info_access_extension() { const std::string_view ocsp_uri{"http://staging.ocsp.d-trust.net"}; // create a CA - auto ca_key = make_a_private_key(sig_algo); - const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, Test::rng()); - Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, Test::rng()); + auto ca_key = make_a_private_key(sig_algo, *rng); + const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, *rng); + Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng); // create a certificate with only caIssuer information - auto key = make_a_private_key(sig_algo); + auto key = make_a_private_key(sig_algo, *rng); Botan::X509_Cert_Options opts1 = req_opts1(sig_algo); opts1.extensions.add(std::make_unique("", ca_issuers)); - Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, Test::rng()); + Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts1, *key, hash_fn, *rng); - Botan::X509_Certificate cert = ca.sign_request(req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01)); if(!result.test_eq("number of ca_issuers URIs", cert.ca_issuers().size(), 2)) { return result; @@ -636,9 +640,9 @@ Test::Result test_x509_encode_authority_info_access_extension() { Botan::X509_Cert_Options opts2 = req_opts1(sig_algo); opts2.extensions.add(std::make_unique(ocsp_uri)); - req = Botan::X509::create_cert_req(opts2, *key, hash_fn, Test::rng()); + req = Botan::X509::create_cert_req(opts2, *key, hash_fn, *rng); - cert = ca.sign_request(req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01)); result.confirm("OCSP URI available", !cert.ocsp_responder().empty()); result.confirm("no CA Issuer URI available", cert.ca_issuers().empty()); @@ -648,9 +652,9 @@ Test::Result test_x509_encode_authority_info_access_extension() { Botan::X509_Cert_Options opts3 = req_opts1(sig_algo); opts3.extensions.add(std::make_unique(ocsp_uri, ca_issuers)); - req = Botan::X509::create_cert_req(opts3, *key, hash_fn, Test::rng()); + req = Botan::X509::create_cert_req(opts3, *key, hash_fn, *rng); - cert = ca.sign_request(req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01)); result.confirm("OCSP URI available", !cert.ocsp_responder().empty()); result.confirm("CA Issuer URI available", !cert.ca_issuers().empty()); @@ -707,6 +711,8 @@ Test::Result test_padding_config() { // Throughout the test, some synonyms for EMSA4 are used, e.g. PSSR, EMSA-PSS Test::Result test_result("X509 Padding Config"); + auto rng = Test::new_rng(__func__); + Botan::DataSource_Stream key_stream(Test::data_file("x509/misc/rsa_key.pem")); auto sk = Botan::PKCS8::load_key(key_stream); @@ -714,14 +720,14 @@ Test::Result test_padding_config() { Botan::X509_Cert_Options opt("TESTCA"); opt.CA_key(); - Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng()); + Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng); test_result.test_eq("CA certificate signature algorithm (default)", ca_cert_def.signature_algorithm().oid().to_formatted_string(), "RSA/EMSA3(SHA-512)"); // Create X509 CA certificate; RSA-PSS is explicitly set opt.set_padding_scheme("PSSR"); - Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng()); + Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng); test_result.test_eq("CA certificate signature algorithm (explicit)", ca_cert_exp.signature_algorithm().oid().to_formatted_string(), "RSA/EMSA4"); @@ -730,7 +736,7 @@ Test::Result test_padding_config() { // Try to set a padding scheme that is not supported for signing with the given key type opt.set_padding_scheme("EMSA2"); try { - Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng()); + Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", *rng); test_result.test_failure("Could build CA cert with invalid encoding scheme EMSA1 for key type " + sk->algo_name()); } catch(const Botan::Invalid_Argument& e) { @@ -750,14 +756,14 @@ Test::Result test_padding_config() { // Prepare a signing request for the end certificate Botan::X509_Cert_Options req_opt("endpoint"); req_opt.set_padding_scheme("EMSA4(SHA-512,MGF1,64)"); - Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", Test::rng()); + Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", *rng); test_result.test_eq("Certificate request signature algorithm", end_req.signature_algorithm().oid().to_formatted_string(), "RSA/EMSA4"); // Create X509 CA object: will fail as the chosen hash functions differ try { - Botan::X509_CA ca_fail(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-256)", Test::rng()); + Botan::X509_CA ca_fail(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-256)", *rng); test_result.test_failure("Configured conflicting hash functions for CA"); } catch(const Botan::Invalid_Argument& e) { test_result.test_eq( @@ -767,28 +773,28 @@ Test::Result test_padding_config() { } // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. EMSA3 - Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", Test::rng()); - Botan::X509_Certificate end_cert_emsa3 = ca_def.sign_request(end_req, Test::rng(), not_before, not_after); + Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", *rng); + Botan::X509_Certificate end_cert_emsa3 = ca_def.sign_request(end_req, *rng, not_before, not_after); test_result.test_eq("End certificate signature algorithm", end_cert_emsa3.signature_algorithm().oid().to_formatted_string(), "RSA/EMSA3(SHA-512)"); // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is different from the CA certificate's scheme - Botan::X509_CA ca_diff(ca_cert_def, (*sk), "SHA-512", "EMSA-PSS", Test::rng()); - Botan::X509_Certificate end_cert_diff_emsa4 = ca_diff.sign_request(end_req, Test::rng(), not_before, not_after); + Botan::X509_CA ca_diff(ca_cert_def, (*sk), "SHA-512", "EMSA-PSS", *rng); + Botan::X509_Certificate end_cert_diff_emsa4 = ca_diff.sign_request(end_req, *rng, not_before, not_after); test_result.test_eq("End certificate signature algorithm", end_cert_diff_emsa4.signature_algorithm().oid().to_formatted_string(), "RSA/EMSA4"); // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is identical to the CA certificate's scheme - Botan::X509_CA ca_exp(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-512,MGF1,64)", Test::rng()); - Botan::X509_Certificate end_cert_emsa4 = ca_exp.sign_request(end_req, Test::rng(), not_before, not_after); + Botan::X509_CA ca_exp(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-512,MGF1,64)", *rng); + Botan::X509_Certificate end_cert_emsa4 = ca_exp.sign_request(end_req, *rng, not_before, not_after); test_result.test_eq("End certificate signature algorithm", end_cert_emsa4.signature_algorithm().oid().to_formatted_string(), "RSA/EMSA4"); // Check CRL signature algorithm - Botan::X509_CRL crl = ca_exp.new_crl(Test::rng()); + Botan::X509_CRL crl = ca_exp.new_crl(*rng); test_result.test_eq("CRL signature algorithm", crl.signature_algorithm().oid().to_formatted_string(), "RSA/EMSA4"); // sanity check for verification, the heavy lifting is done in the other unit tests @@ -806,7 +812,8 @@ Test::Result test_padding_config() { Test::Result test_pkcs10_ext(const Botan::Private_Key& key, const std::string& sig_padding, - const std::string& hash_fn) { + const std::string& hash_fn, + Botan::RandomNumberGenerator& rng) { Test::Result result("PKCS10 extensions"); Botan::X509_Cert_Options opts; @@ -820,7 +827,7 @@ Test::Result test_pkcs10_ext(const Botan::Private_Key& key, opts.extensions.add(std::make_unique(alt_name)); - Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, key, hash_fn, Test::rng()); + Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, key, hash_fn, rng); std::vector alt_dns_names = req.subject_alt_name().get_attribute("DNS"); @@ -839,11 +846,12 @@ Test::Result test_pkcs10_ext(const Botan::Private_Key& key, Test::Result test_x509_cert(const Botan::Private_Key& ca_key, const std::string& sig_algo, const std::string& sig_padding, - const std::string& hash_fn) { + const std::string& hash_fn, + Botan::RandomNumberGenerator& rng) { Test::Result result("X509 Unit"); /* Create the self-signed cert */ - const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng()); + const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng); { result.confirm("ca key usage cert", ca_cert.constraints().includes(Botan::Key_Constraints::KeyCertSign)); @@ -851,46 +859,42 @@ Test::Result test_x509_cert(const Botan::Private_Key& ca_key, } /* Create user #1's key and cert request */ - auto user1_key = make_a_private_key(sig_algo); + auto user1_key = make_a_private_key(sig_algo, rng); Botan::PKCS10_Request user1_req = - Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, Test::rng()); + Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, rng); result.test_eq("PKCS10 challenge password parsed", user1_req.challenge_password(), "zoom"); /* Create user #2's key and cert request */ - auto user2_key = make_a_private_key(sig_algo); + auto user2_key = make_a_private_key(sig_algo, rng); - Botan::PKCS10_Request user2_req = - Botan::X509::create_cert_req(req_opts2(sig_padding), *user2_key, hash_fn, Test::rng()); + Botan::PKCS10_Request user2_req = Botan::X509::create_cert_req(req_opts2(sig_padding), *user2_key, hash_fn, rng); // /* Create user #3's key and cert request */ - auto user3_key = make_a_private_key(sig_algo); + auto user3_key = make_a_private_key(sig_algo, rng); - Botan::PKCS10_Request user3_req = - Botan::X509::create_cert_req(req_opts3(sig_padding), *user3_key, hash_fn, Test::rng()); + Botan::PKCS10_Request user3_req = Botan::X509::create_cert_req(req_opts3(sig_padding), *user3_key, hash_fn, rng); /* Create the CA object */ - Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng()); + Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng); const BigInt user1_serial = 99; /* Sign the requests to create the certs */ Botan::X509_Certificate user1_cert = - ca.sign_request(user1_req, Test::rng(), user1_serial, from_date(-1, 01, 01), from_date(2, 01, 01)); + ca.sign_request(user1_req, rng, user1_serial, from_date(-1, 01, 01), from_date(2, 01, 01)); result.test_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1); result.test_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99)); - Botan::X509_Certificate user2_cert = - ca.sign_request(user2_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + Botan::X509_Certificate user2_cert = ca.sign_request(user2_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01)); - Botan::X509_Certificate user3_cert = - ca.sign_request(user3_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + Botan::X509_Certificate user3_cert = ca.sign_request(user3_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01)); // user#1 creates a self-signed cert on the side const auto user1_ss_cert = - Botan::X509::create_self_signed_cert(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, Test::rng()); + Botan::X509::create_self_signed_cert(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, rng); { auto constraints = req_opts1(sig_algo).constraints; @@ -905,7 +909,7 @@ Test::Result test_x509_cert(const Botan::Private_Key& ca_key, result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true); Botan::X509_Certificate user1_cert_differ = - ca.sign_request(user1_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01)); result.test_eq("certificate differs", user1_cert == user1_cert_differ, false); @@ -935,7 +939,7 @@ Test::Result test_x509_cert(const Botan::Private_Key& ca_key, "subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1); result.test_eq("subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0)); - const Botan::X509_CRL crl1 = ca.new_crl(Test::rng()); + const Botan::X509_CRL crl1 = ca.new_crl(rng); /* Verify the certs */ Botan::Path_Validation_Restrictions restrictions(false, 80); @@ -969,7 +973,7 @@ Test::Result test_x509_cert(const Botan::Private_Key& ca_key, revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::CessationOfOperation)); revoked.push_back(user2_cert); - const Botan::X509_CRL crl2 = ca.update_crl(crl1, revoked, Test::rng()); + const Botan::X509_CRL crl2 = ca.update_crl(crl1, revoked, rng); store.add_crl(crl2); @@ -984,7 +988,7 @@ Test::Result test_x509_cert(const Botan::Private_Key& ca_key, revoked.clear(); revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CRL_Code::RemoveFromCrl)); - Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, Test::rng()); + Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, rng); store.add_crl(crl3); @@ -999,28 +1003,30 @@ Test::Result test_x509_cert(const Botan::Private_Key& ca_key, return result; } -Test::Result test_usage(const Botan::Private_Key& ca_key, const std::string& sig_algo, const std::string& hash_fn) { +Test::Result test_usage(const Botan::Private_Key& ca_key, + const std::string& sig_algo, + const std::string& hash_fn, + Botan::RandomNumberGenerator& rng) { using Botan::Key_Constraints; using Botan::Usage_Type; Test::Result result("X509 Usage"); /* Create the self-signed cert */ - const Botan::X509_Certificate ca_cert = - Botan::X509::create_self_signed_cert(ca_opts(), ca_key, hash_fn, Test::rng()); + const Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), ca_key, hash_fn, rng); /* Create the CA object */ - const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, Test::rng()); + const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, rng); - auto user1_key = make_a_private_key(sig_algo); + auto user1_key = make_a_private_key(sig_algo, rng); Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing"); opts.constraints = Key_Constraints::DigitalSignature; - const Botan::PKCS10_Request user1_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, Test::rng()); + const Botan::PKCS10_Request user1_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, rng); const Botan::X509_Certificate user1_cert = - ca.sign_request(user1_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + ca.sign_request(user1_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01)); // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign result.test_eq( @@ -1034,10 +1040,10 @@ Test::Result test_usage(const Botan::Private_Key& ca_key, const std::string& sig opts.constraints = Key_Constraints(Key_Constraints::DigitalSignature | Key_Constraints::CrlSign); - const Botan::PKCS10_Request mult_usage_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, Test::rng()); + const Botan::PKCS10_Request mult_usage_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, rng); const Botan::X509_Certificate mult_usage_cert = - ca.sign_request(mult_usage_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + ca.sign_request(mult_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01)); // cert allows multiple usages, so each one of them as well as both together should be allowed result.confirm("key usage multiple digitalSignature allowed", @@ -1050,10 +1056,10 @@ Test::Result test_usage(const Botan::Private_Key& ca_key, const std::string& sig opts.constraints = Key_Constraints(); - const Botan::PKCS10_Request no_usage_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, Test::rng()); + const Botan::PKCS10_Request no_usage_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, rng); const Botan::X509_Certificate no_usage_cert = - ca.sign_request(no_usage_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + ca.sign_request(no_usage_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01)); // cert allows every usage result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DigitalSignature)); @@ -1064,10 +1070,10 @@ Test::Result test_usage(const Botan::Private_Key& ca_key, const std::string& sig // cert allows data encryption opts.constraints = Key_Constraints(Key_Constraints::KeyEncipherment | Key_Constraints::DataEncipherment); - const Botan::PKCS10_Request enc_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, Test::rng()); + const Botan::PKCS10_Request enc_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, rng); const Botan::X509_Certificate enc_cert = - ca.sign_request(enc_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + ca.sign_request(enc_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01)); result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION)); result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH)); @@ -1079,19 +1085,20 @@ Test::Result test_usage(const Botan::Private_Key& ca_key, const std::string& sig Test::Result test_self_issued(const Botan::Private_Key& ca_key, const std::string& sig_algo, const std::string& sig_padding, - const std::string& hash_fn) { + const std::string& hash_fn, + Botan::RandomNumberGenerator& rng) { using Botan::Key_Constraints; Test::Result result("X509 Self Issued"); // create the self-signed cert const Botan::X509_Certificate ca_cert = - Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng()); + Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng); /* Create the CA object */ - const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng()); + const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng); - auto user_key = make_a_private_key(sig_algo); + auto user_key = make_a_private_key(sig_algo, rng); // create a self-issued certificate, that is, a certificate with subject dn == issuer dn, // but signed by a CA, not signed by it's own private key @@ -1099,10 +1106,10 @@ Test::Result test_self_issued(const Botan::Private_Key& ca_key, opts.constraints = Key_Constraints::DigitalSignature; opts.set_padding_scheme(sig_padding); - const Botan::PKCS10_Request self_issued_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, Test::rng()); + const Botan::PKCS10_Request self_issued_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, rng); const Botan::X509_Certificate self_issued_cert = - ca.sign_request(self_issued_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + ca.sign_request(self_issued_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01)); // check that this chain can can be verified successfully const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate()); @@ -1270,17 +1277,17 @@ class String_Extension final : public Botan::Certificate_Extension { Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key, const std::string& sig_algo, const std::string& sig_padding, - const std::string& hash_fn) { + const std::string& hash_fn, + Botan::RandomNumberGenerator& rng) { Test::Result result("X509 Custom DN"); /* Create the self-signed cert */ - Botan::X509_Certificate ca_cert = - Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng()); + Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng); /* Create the CA object */ - Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng()); + Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng); - auto user_key = make_a_private_key(sig_algo); + auto user_key = make_a_private_key(sig_algo, rng); Botan::X509_DN subject_dn; @@ -1295,7 +1302,7 @@ Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key, Botan::Extensions extensions; Botan::PKCS10_Request req = - Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, Test::rng(), sig_padding); + Botan::PKCS10_Request::create(*user_key, subject_dn, extensions, hash_fn, rng, sig_padding); const Botan::X509_DN& req_dn = req.subject_dn(); @@ -1311,7 +1318,7 @@ Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key, Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Type::UtcTime); Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Type::UtcTime); - auto cert = ca.sign_request(req, Test::rng(), not_before, not_after); + auto cert = ca.sign_request(req, rng, not_before, not_after); const Botan::X509_DN& cert_dn = cert.subject_dn(); @@ -1330,17 +1337,17 @@ Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key, Test::Result test_x509_extensions(const Botan::Private_Key& ca_key, const std::string& sig_algo, const std::string& sig_padding, - const std::string& hash_fn) { + const std::string& hash_fn, + Botan::RandomNumberGenerator& rng) { using Botan::Key_Constraints; Test::Result result("X509 Extensions"); /* Create the self-signed cert */ - Botan::X509_Certificate ca_cert = - Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng()); + Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, rng); /* Create the CA object */ - Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, Test::rng()); + Botan::X509_CA ca(ca_cert, ca_key, hash_fn, sig_padding, rng); /* Prepare CDP extension */ std::vector cdp_urls = { @@ -1356,7 +1363,7 @@ Test::Result test_x509_extensions(const Botan::Private_Key& ca_key, dps.emplace_back(dp); } - auto user_key = make_a_private_key(sig_algo); + auto user_key = make_a_private_key(sig_algo, rng); Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing"); opts.constraints = Key_Constraints::DigitalSignature; @@ -1371,8 +1378,7 @@ Test::Result test_x509_extensions(const Botan::Private_Key& ca_key, opts.set_padding_scheme(sig_padding); /* Create a self-signed certificate */ - const Botan::X509_Certificate self_signed_cert = - Botan::X509::create_self_signed_cert(opts, *user_key, hash_fn, Test::rng()); + const Botan::X509_Certificate self_signed_cert = Botan::X509::create_self_signed_cert(opts, *user_key, hash_fn, rng); result.confirm("Extensions::extension_set true for Key_Usage", self_signed_cert.v3_extensions().extension_set(ku_oid)); @@ -1404,11 +1410,11 @@ Test::Result test_x509_extensions(const Botan::Private_Key& ca_key, } } - const Botan::PKCS10_Request user_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, Test::rng()); + const Botan::PKCS10_Request user_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, rng); /* Create a CA-signed certificate */ const Botan::X509_Certificate ca_signed_cert = - ca.sign_request(user_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + ca.sign_request(user_req, rng, from_date(-1, 01, 01), from_date(2, 01, 01)); // check if known Key_Usage extension is present in CA-signed cert result.confirm("Extensions::extension_set true for Key_Usage", ca_signed_cert.v3_extensions().extension_set(ku_oid)); @@ -1443,7 +1449,7 @@ Test::Result test_x509_extensions(const Botan::Private_Key& ca_key, return result; } -Test::Result test_hashes(const Botan::Private_Key& key, const std::string& hash_fn) { +Test::Result test_hashes(const Botan::Private_Key& key, const std::string& hash_fn, Botan::RandomNumberGenerator& rng) { Test::Result result("X509 Hashes"); struct TestData { @@ -1477,16 +1483,16 @@ Test::Result test_hashes(const Botan::Private_Key& key, const std::string& hash_ Botan::X509_Cert_Options opts{a.issuer}; opts.CA_key(); - const Botan::X509_Certificate issuer_cert = Botan::X509::create_self_signed_cert(opts, key, hash_fn, Test::rng()); + const Botan::X509_Certificate issuer_cert = Botan::X509::create_self_signed_cert(opts, key, hash_fn, rng); result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash); result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash); - const Botan::X509_CA ca(issuer_cert, key, hash_fn, Test::rng()); + const Botan::X509_CA ca(issuer_cert, key, hash_fn, rng); const Botan::PKCS10_Request req = - Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, Test::rng()); + Botan::X509::create_cert_req(Botan::X509_Cert_Options(a.subject), key, hash_fn, rng); const Botan::X509_Certificate subject_cert = - ca.sign_request(req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01)); + ca.sign_request(req, rng, from_date(-1, 01, 01), from_date(2, 01, 01)); result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash); result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash); @@ -1514,6 +1520,8 @@ class X509_Cert_Unit_Tests final : public Test { std::vector run() override { std::vector results; + auto& rng = this->rng(); + const std::string sig_algos[]{ "RSA", "DSA", "ECDSA", "ECGDSA", "ECKCDSA", "GOST-34.10", "Ed25519", "Dilithium"}; @@ -1532,18 +1540,18 @@ class X509_Cert_Unit_Tests final : public Test { hash = "SHAKE-256(512)"; } - auto key = make_a_private_key(algo); + auto key = make_a_private_key(algo, rng); if(key == nullptr) { continue; } - results.push_back(test_hashes(*key, hash)); + results.push_back(test_hashes(*key, hash, rng)); results.push_back(test_valid_constraints(*key, algo)); Test::Result usage_result("X509 Usage"); try { - usage_result.merge(test_usage(*key, algo, hash)); + usage_result.merge(test_usage(*key, algo, hash, rng)); } catch(std::exception& e) { usage_result.test_failure("test_usage " + algo, e.what()); } @@ -1553,7 +1561,7 @@ class X509_Cert_Unit_Tests final : public Test { Test::Result cert_result("X509 Unit"); try { - cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash)); + cert_result.merge(test_x509_cert(*key, algo, padding_scheme, hash, rng)); } catch(std::exception& e) { cert_result.test_failure("test_x509_cert " + algo, e.what()); } @@ -1561,7 +1569,7 @@ class X509_Cert_Unit_Tests final : public Test { Test::Result pkcs10_result("PKCS10 extensions"); try { - pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme, hash)); + pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme, hash, rng)); } catch(std::exception& e) { pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what()); } @@ -1569,7 +1577,7 @@ class X509_Cert_Unit_Tests final : public Test { Test::Result self_issued_result("X509 Self Issued"); try { - self_issued_result.merge(test_self_issued(*key, algo, padding_scheme, hash)); + self_issued_result.merge(test_self_issued(*key, algo, padding_scheme, hash, rng)); } catch(std::exception& e) { self_issued_result.test_failure("test_self_issued " + algo, e.what()); } @@ -1577,7 +1585,7 @@ class X509_Cert_Unit_Tests final : public Test { Test::Result extensions_result("X509 Extensions"); try { - extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash)); + extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme, hash, rng)); } catch(std::exception& e) { extensions_result.test_failure("test_extensions " + algo, e.what()); } @@ -1585,7 +1593,7 @@ class X509_Cert_Unit_Tests final : public Test { Test::Result custom_dn_result("X509 Custom DN"); try { - custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash)); + custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme, hash, rng)); } catch(std::exception& e) { custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what()); } @@ -1599,7 +1607,7 @@ class X509_Cert_Unit_Tests final : public Test { const std::vector enc_algos = {"DH", "ECDH", "ElGamal", "Kyber", "FrodoKEM", "ClassicMcEliece"}; for(const std::string& algo : enc_algos) { - auto key = make_a_private_key(algo); + auto key = make_a_private_key(algo, rng); if(key) { results.push_back(test_valid_constraints(*key, algo));